home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Fixation 1.3 / net.c < prev    next >
Text File  |  1995-09-16  |  75KB  |  2,747 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     net.c
  4.  
  5.     This reusable and reentrant module handles low-level communications with 
  6.     TCP/IP network servers, using a simple "net ASCII" command/response 
  7.     stream model. It also exports several functions for using FTP data streams,
  8.     and several functions for doing domain name resolver tasks. 
  9.     
  10.     The module hides the complexity and peculiarities of Open Transport and the 
  11.     MacTCP device driver. If Open Transport is available and we are running in
  12.     native PowerPC mode, it is used. Otherwise, MacTCP is used. 
  13.     
  14.     The module also manages many of the common protocol details of communicating
  15.     with net ASCII servers: 
  16.     
  17.     Handling initial server "hello" messages.
  18.     Handling final server "QUIT" commands and all the details of graceful
  19.         asynchronous stream close operations.
  20.     Mapping between CR line terminators and CRLF line terminators.
  21.     Decoding numeric server response codes.
  22.     Skipping server comments (response lines with "-" following the response code).
  23.     Supplying terminating "." lines when sending blocks of text.
  24.     Recognizing and discarding terminating "." lines when reading blocks of text.
  25.     Mapping between leading "." characters and leading ".." characters on text lines. 
  26.     
  27.     The module emphasizes simplicity at the expense of power. There are many things 
  28.     you can do by calling Open Transport or MacTCP directly which you cannot do with 
  29.     this module. The module is *not* a general purpose interface to Open Transport or
  30.     MacTCP designed to meet the needs of all possible Mac TCP/IP networking programs. 
  31.     It is only a convenient interface for writing clients for typical simple net ASCII 
  32.     servers.
  33.     
  34.     The following mandatory functions handle initialization, idle time, and
  35.     termination tasks.
  36.     
  37.         NetInit - Initialize the module.
  38.         NetIdle - Handle idle time tasks.
  39.         NetTerm - Terminate the module.
  40.     
  41.     The following functions work with Net ASCII command/response streams.
  42.  
  43.         NetOpen - Open a stream.
  44.         NetClose - Close a stream.
  45.         NetCommand - Send a command and get the response.
  46.         NetGetExtraResponse - Get an extra command response.
  47.         NetBatchedCommands - Send several commands in a batch and
  48.             get and process the responses.
  49.         NetPutText - Send command text.
  50.         NetGetText - Get response text.
  51.         
  52.     The following functions work with FTP data streams. They are used by 
  53.     the ftp.c module.
  54.     .
  55.         NetFTPDataPassiveOpen - Open an FTP passive data stream.
  56.         NetFTPDataClose - Close an FTP data stream.
  57.         NetFTPDataWaitForConnection - Wait for the FTP server to open
  58.             its end of an FTP passive data stream.
  59.         NetGetFTPData - Get FTP data.
  60.         NetSendFTPData - Send FTP data.
  61.         
  62.     The following functions handle simple domain name resolver and related tasks.
  63.     
  64.         NetGetMyAddr - Get my IP address.
  65.         NetGetMyAddrStr - Get my IP address as a dotted-decimal string.
  66.         NetGetMyName - Get my domain name.
  67.         NetNameToAddr - Convert a domain name to an IP address.
  68.         NetAddrToName - Convert an IP address to a domain name.
  69.         
  70.     Finally, the module exports the following utility functions:
  71.     
  72.         NetMacTCPDNROperationInProgress - Determine if a MacTCP DNR operation 
  73.             is in progress.
  74.         NetGetServerErrInfo - Get server error information.
  75.         NetGetStreamStats - Get stream stats (bytes in/out).
  76.         NetHaveOT - Determine whether we are using Open Transport or MacTCP.
  77.     
  78.     A "stream" is an abstraction representing a bidirectional network connection
  79.     to a net ASCII TCP/IP server. 
  80.     
  81.     With Open Transport, the notion of a "stream" in this module is basically
  82.     equivalent to a "TCP endpoint". The "NetOpen" function opens an endpoint, 
  83.     binds it to a TCP port,  and creates a connection. The "NetClose" function
  84.     does an orderly disconnect and closes the endpoint.
  85.     
  86.     With MacTCP, the notion of a "stream" in this module combines the 
  87.     separate MacTCP notions of "stream" and "connection". When calling MacTCP 
  88.     directly, you "create" and "release" streams and "open" and "close" connections. 
  89.     These operations are combined in the net.c module. The "NetOpen" function both
  90.     creates a stream and opens a connection. The "NetClose" function both
  91.     closes the connection and releases the stream.
  92.     
  93.     A stream is represented as a variable of type "NetStreamRef". These stream
  94.     references are opaque. You may copy them and pass them as parameters to 
  95.     functions in net.c, but you are prohibited from accessing the contents of
  96.     the memory blocks pointed to by the references. Only the functions in 
  97.     net.c are permitted to manipulate the contents of these blocks.
  98.     
  99.     The functions return a value of type OSErr as the function result:
  100.     
  101.         noErr                    no error occurred
  102.         netOpenDriverErr        NetInit could not initialize the network driver
  103.         netOpenStreamErr        stream open failed
  104.         netLostConnectionErr    stream was unexpectedly closed or aborted by server
  105.         netDNRErr                DNR failed to resolve name or address
  106.         netTruncatedErr            incoming text was truncated
  107.         other                    any other OS or toolbox error
  108.     
  109.     If any error occurs, the stream is aborted before returning to the caller. 
  110.     "Aborted" means that the server connection is closed abruptly, without going 
  111.     through the usual orderly TCP stream teardown process. The stream is also 
  112.     deallocated. In this case, you must not attempt to reuse the stream reference, 
  113.     since the stream no longer exists. You must perform careful error checking.
  114.     
  115.     The "expected" Open Transport and MacTCP error codes are translated into either
  116.     regular MacOS error codes (e.g., kENOMEMErr -> memFullErr) or into the
  117.     special "net" error codes (e.g., authNameErr -> netDNRErr). This
  118.     gives the clients of this module a uniform set of error codes which are the
  119.     same for both Open Transport and MacTCP. Open Transport and MacTCP error
  120.     codes which are not translated are "unexpected" errors which should not
  121.     occur, and if they do, indicate an error in the code in this module.
  122.     
  123.     Copyright © 1994-1995, Northwestern University.
  124.  
  125. ----------------------------------------------------------------------------*/
  126.  
  127. #include <stdlib.h>
  128. #include <string.h>
  129. #include <ctype.h>
  130. #include <stdio.h>
  131.  
  132. #include "MyMacTCP.h"
  133.  
  134. #include "OpenTransport.h"
  135. #include "OpenTptInternet.h"
  136.  
  137. #include "def.h"
  138. #include "net.h"
  139. #include "memutil.h"
  140. #include "strutil.h"
  141.  
  142.  
  143.  
  144. /*    Constants. */
  145.  
  146. #define kBufSize             (4*1024)            /* buffer size */
  147. #define kBufSizeDiv2        (kBufSize/2)        /* buffer size div 2 */
  148. #define kMinTCPBufSize        (16*1024)            /* minimum MacTCP stream buffer size */
  149.  
  150.  
  151.  
  152. /*    Types. */
  153.  
  154. typedef struct TStream {            /* Stream info */
  155.  
  156.     TCPiopb pBlock;                    /* MacTCP parameter block (must be first, unused with
  157.                                        Open Transport) */
  158.     
  159.     /* Fields used with both Open Transport and MacTCP. */
  160.                                        
  161.     struct TStream *next;            /* pointer to next stream in list of all open streams */
  162.     struct TStream *nextClose;        /* pointer to next stream in list of closing streams */
  163.     struct TStream *nextFree;        /* pointer to next stream in list of available preallocated
  164.                                        streams */
  165.     unsigned long serverAddr;        /* IP address of server */
  166.     unsigned short serverPort;        /* port number on server */
  167.     unsigned short localPort;        /* local port number */
  168.     char buf[kBufSize];                /* data buffer */
  169.     char *in;                        /* pointer to next location in data buffer in
  170.                                        which incoming network bytes should be stored */
  171.     char *out;                        /* pointer to next location in data buffer from
  172.                                        which bytes should be removed and delivered
  173.                                        to our clients (note: we always have out <= in) */
  174.     Boolean fromFreeList;            /* true if this buffer was obtained from the free list */
  175.     CStr255 command;                /* last command sent on stream */
  176.     CStr255 response;                /* last response received on stream */
  177.     long responseCode;                /* last response code received on stream */
  178.     Boolean closing;                /* true when closing stream */
  179.     Boolean otherSideHasClosed;        /* true when other side has closed its end of 
  180.                                        the stream */
  181.     Boolean weHaveClosed;            /* true when we have closed our end of the stream */
  182.     Boolean release;                /* true when stream should be released */
  183.     long bytesIn;                    /* number of bytes received on stream */
  184.     long bytesOut;                    /* number of bytes sent on stream */
  185.     
  186.     /* Fields used only with Open Transport. */
  187.     
  188.     EndpointRef ref;                /* endpoint reference */
  189.     Boolean complete;                /* true when asynch operation has completed */
  190.     OTEventCode code;                /* event code */
  191.     OTResult result;                /* result */
  192.     void *cookie;                    /* cookie */
  193.     TCall *call;                    /* pointer to call structure */
  194.     TBind *bindReq;                    /* pointer to bind request structure */
  195.     TBind *bindRet;                    /* pointer to bind return structure */
  196.     
  197.     /* Fields used only with MacTCP. */
  198.     
  199.     long myA5;                        /* our A5 register */
  200.     StreamPtr tcpStream;            /* MacTCP stream pointer */
  201.     struct wdsEntry wds[2];            /* MacTCP write data structure */
  202.     char mactcpBuf[1];                /* MacTCP buffer - size depends on MTU (must be last) */
  203.  
  204. } TStream, *TStreamPtr;
  205.  
  206. typedef struct TMyOTInetSvcInfo {        /* Open Transport Internet services provider info */
  207.     InetSvcRef ref;            /* provider reference */
  208.     Boolean complete;        /* true when asynch operation has completed */
  209.     OTResult result;        /* result code */
  210.     void *cookie;            /* cookie */
  211. } TMyOTInetSvcInfo;
  212.  
  213.  
  214.  
  215. /*    Global variables. */
  216.  
  217. static Boolean gHaveOT;                    /* true if we have Open Transport */
  218. static NetGiveTimeFunction gGiveTime;    /* pointer to GiveTime function */
  219. static NetLogFunction gLog;                /* pointer to logging function, or nil if none */
  220. static TStreamPtr gAll = nil;            /* pointer to list of open streams */
  221. static TStreamPtr gClose = nil;            /* pointer to list of closing streams */
  222. static TStreamPtr gFree = nil;            /* pointer to list of available preallocated 
  223.                                            stream buffers */
  224. static long gStreamBufSize;                /* stream buffer size */
  225.  
  226. static short gRefNum;                    /* MacTCP driver reference number */
  227. static long gTCPBufSize;                /* TCP buffer size for MacTCP */
  228. static Boolean gMacTCPDNROperationInProgress = false;
  229.                                         /* True when MacTCP DNR operation in progress */
  230.                                         
  231. static ResultUPP gMacTCPDNRResultProcUPP = nil;
  232. static TCPIOCompletionUPP gMacTCPCloseStreamCompletionRoutineUPP = nil;
  233. static UniversalProcPtr gOldExitToShellUPP;
  234. static UniversalProcPtr gMyExitToShellUPP;
  235.  
  236.  
  237.  
  238. /*----------------------------------------------------------------------------
  239.     TranslateErrorCode 
  240.     
  241.     Translate error codes.
  242.     
  243.     Entry:    err = Open Transport, MacTCP, or OS error code.
  244.     
  245.     Exit:    function result = translated error code.
  246. ----------------------------------------------------------------------------*/
  247.  
  248. static OSErr TranslateErrorCode (OSErr err)
  249. {
  250.     switch (err) {
  251.         case kENOMEMErr:
  252.             return memFullErr;
  253.         case noNameServer:
  254.         case authNameErr:
  255.         case noAnsErr:
  256.         case dnrErr:
  257.         case nameSyntaxErr:
  258.             return netDNRErr;
  259.         case openFailed:
  260.             return netOpenStreamErr;
  261.         default:
  262.             return err;
  263.     }
  264. }
  265.  
  266.  
  267.  
  268. /*----------------------------------------------------------------------------
  269.     LogMessage
  270.     
  271.     Log a message.
  272.     
  273.     Entry:    s = pointer to stream.
  274.             logEntryType = 
  275.                 'C' if command.
  276.                 'R' if response.
  277.                 ' ' if open/close operation.
  278.             str = command or response string or open/close message string.
  279. ----------------------------------------------------------------------------*/
  280.  
  281. static void LogMessage (TStreamPtr s, char logEntryType, char *str)
  282. {
  283.     if (gLog != nil) (*gLog)(logEntryType, s->serverAddr, s->serverPort,
  284.         s->localPort, str);
  285. }
  286.  
  287.  
  288.  
  289. /*----------------------------------------------------------------------------
  290.     NewStreamBuffer 
  291.     
  292.     Allocate a stream buffer.
  293.     
  294.     Exit:    function result = error code.
  295.             *s = pointer to new stream buffer.
  296. ----------------------------------------------------------------------------*/
  297.  
  298. static OSErr NewStreamBuffer (TStreamPtr *s)
  299. {
  300.     OSErr err = noErr;
  301.  
  302.     if (gFree == nil) {
  303.         err = MyNewPtr(gStreamBufSize, s);
  304.         if (err != noErr) return err;
  305.     } else {
  306.         *s = gFree;
  307.         gFree = gFree->nextFree;
  308.         memset(*s, 0, sizeof(TStream));
  309.         (*s)->fromFreeList = true;
  310.     }
  311.     if (!gHaveOT) (*s)->myA5 = SetCurrentA5();
  312.     return noErr;
  313. }
  314.  
  315.  
  316.  
  317. /*----------------------------------------------------------------------------
  318.     DisposeStreamBuffer 
  319.     
  320.     Dispose a stream buffer.
  321.     
  322.     Entry:    s = pointer to stream buffer.
  323. ----------------------------------------------------------------------------*/
  324.  
  325. static void DisposeStreamBuffer (TStreamPtr s)
  326. {
  327.     if (s == nil) return;
  328.     
  329.     if (gHaveOT) {
  330.         if (s->call != nil) OTFree(s->call, T_CALL);
  331.         if (s->bindReq != nil) OTFree(s->bindReq, T_BIND);
  332.         if (s->bindRet != nil) OTFree(s->bindRet, T_BIND);
  333.     }
  334.     
  335.     if (s->fromFreeList) {
  336.         s->nextFree = gFree;
  337.         gFree = s;
  338.     } else {
  339.         MyDisposePtr(s);
  340.     }
  341. }
  342.  
  343.  
  344.  
  345. /*----------------------------------------------------------------------------
  346.     MyOTStreamNotifyProc 
  347.     
  348.     Open Transport notifier proc for TCP streams.
  349.     
  350.     Entry:    s = pointer to stream.
  351.             code = OT event code.
  352.             result = OT result.
  353.             cookie = OT cookie.
  354. ----------------------------------------------------------------------------*/
  355.  
  356. static pascal void MyOTStreamNotifyProc (TStreamPtr s, OTEventCode code,
  357.     OTResult result, void *cookie)
  358. {
  359.     switch (code) {
  360.         case T_DISCONNECT:
  361.             /* Other side has aborted. */
  362.             s->otherSideHasClosed = true;
  363.             s->complete = true;
  364.             break;
  365.         case T_ORDREL:
  366.             /* Other side has closed. Close our side if necessary. */
  367.             s->otherSideHasClosed = true;
  368.             s->complete = true;
  369.             OTRcvOrderlyDisconnect(s->ref);
  370.             if (!s->weHaveClosed) {
  371.                 OTSndOrderlyDisconnect(s->ref);
  372.                 s->weHaveClosed = true;
  373.             }
  374.             if (s->closing) s->release = true;
  375.             break;
  376.         case T_DATA:
  377.             if (s->closing) {
  378.                 /* Consume and discard response to "QUIT" comand. */
  379.                 do {
  380.                     result = OTRcv(s->ref, s->buf, kBufSize, nil);
  381.                 } while (result >= 0);
  382.             }
  383.             break;
  384.         case T_OPENCOMPLETE:
  385.         case T_BINDCOMPLETE:
  386.         case T_CONNECT:
  387.         case T_PASSCON:
  388.             s->complete = true;
  389.             s->code = code;
  390.             s->result = result;
  391.             s->cookie = cookie;
  392.             break;
  393.     }
  394. }
  395.  
  396.  
  397.  
  398. /*----------------------------------------------------------------------------
  399.     MyOTStreamWait 
  400.     
  401.     Wait for an asynchronous Open Transport stream call to complete.
  402.     
  403.     Entry:    s = pointer to stream.
  404.             returnImmediatelyOnError = true to return immediately if
  405.                 the GiveTime function returns an error.
  406.     
  407.     Exit:    function result = error code.
  408. ----------------------------------------------------------------------------*/
  409.  
  410. static OSErr MyOTStreamWait (TStreamPtr s, Boolean returnImmediatelyOnError)
  411. {
  412.     OSErr err = noErr, giveTimeErr = noErr;
  413.  
  414.     do {
  415.         giveTimeErr = (*gGiveTime)();
  416.         if (err == noErr) err = giveTimeErr;
  417.         if (err != noErr && returnImmediatelyOnError) return err;
  418.     } while (!s->complete);
  419.     if (err == noErr) err = s->result;
  420.     return err;
  421. }
  422.  
  423.  
  424.  
  425. /*----------------------------------------------------------------------------
  426.     MyOTInetSvcNotifyProc 
  427.     
  428.     Open Transport notifier proc for an Internet services provider.
  429.     
  430.     Entry:    svcIfno = pointer to MyOTInetSvcInfo struct.
  431.             code = OT event code.
  432.             result = OT result.
  433.             cookie = OT cookie.
  434. ----------------------------------------------------------------------------*/
  435.  
  436. static pascal void MyOTInetSvcNotifyProc (TMyOTInetSvcInfo *svcInfo, OTEventCode code,
  437.     OTResult result, void *cookie)
  438. {
  439.     switch (code) {
  440.         case T_OPENCOMPLETE:
  441.         case T_DNRSTRINGTOADDRCOMPLETE:
  442.         case T_DNRADDRTONAMECOMPLETE:
  443.             svcInfo->complete = true;
  444.             svcInfo->result = result; 
  445.             svcInfo->cookie = cookie;
  446.             break;
  447.     }
  448. }
  449.  
  450.  
  451.  
  452. /*----------------------------------------------------------------------------
  453.     MyOTInetSvcWait 
  454.     
  455.     Wait for an asynchronous Open Transport Internet services call to complete.
  456.     
  457.     Entry:    svcInfo = pointer to TMyOTInetSvcInfo struct.
  458.     
  459.     Exit:    function result = error code.
  460. ----------------------------------------------------------------------------*/
  461.  
  462. static OSErr MyOTInetSvcWait (TMyOTInetSvcInfo *svcInfo)
  463. {
  464.     OSErr err = noErr;
  465.  
  466.     do {
  467.         err = (*gGiveTime)();
  468.         if (err != noErr) return err;
  469.     } while (!svcInfo->complete);
  470.     return svcInfo->result;
  471. }
  472.  
  473.  
  474.  
  475. /*----------------------------------------------------------------------------
  476.     MyOTOpenInetServices 
  477.     
  478.     Open an Internet services provider.
  479.     
  480.     Entry:    svcInfo = pointer to TMyOTInetSvcInfo struct for this
  481.                 provider.
  482.     
  483.     Exit:    function result = error code.
  484. ----------------------------------------------------------------------------*/
  485.  
  486. static OSErr MyOTOpenInetServices (TMyOTInetSvcInfo *svcInfo)
  487. {
  488.     OSErr err = noErr;
  489.  
  490.     svcInfo->complete = false;
  491.     err = OTAsyncOpenInternetServices(kDefaultInternetServicesPath, 0, 
  492.         MyOTInetSvcNotifyProc, svcInfo);
  493.     if (err != noErr) return err;
  494.     err = MyOTInetSvcWait(svcInfo);
  495.     if (err != noErr) return err;
  496.     svcInfo->ref = svcInfo->cookie;
  497.     return noErr;
  498. }
  499.  
  500.  
  501.  
  502. /*----------------------------------------------------------------------------
  503.     InitMacTCPParamBlock 
  504.     
  505.     Initialize a MacTCP parameter block.
  506.     
  507.     Entry:    pBlock = pointer to parameter block.
  508.             csCode = MacTCP control code (TCPCreate, etc.).
  509. ----------------------------------------------------------------------------*/
  510.  
  511. static void InitMacTCPParamBlock (TCPiopb *pBlock, short csCode)
  512. {
  513.     memset(pBlock, 0, sizeof(TCPiopb));
  514.     pBlock->ioResult = 1;
  515.     pBlock->ioCRefNum = gRefNum;
  516.     pBlock->csCode = csCode;
  517. }
  518.  
  519.  
  520.  
  521. /*----------------------------------------------------------------------------
  522.     CallMacTCP 
  523.     
  524.     Call MacTCP and wait for the call to complete.
  525.     
  526.     Entry:    s = pointer to stream.
  527.             returnImmediatelyOnError = true to return immediately if
  528.                 the GiveTime function returns an error.
  529.     
  530.     Exit:    function result = error code.
  531. ----------------------------------------------------------------------------*/
  532.  
  533. static OSErr CallMacTCP (TStreamPtr s, Boolean returnImmediatelyOnError)
  534. {
  535.     OSErr err = noErr, giveTimeErr = noErr;
  536.  
  537.     PBControlAsync((ParmBlkPtr)&s->pBlock);
  538.     do {
  539.         giveTimeErr = (*gGiveTime)();
  540.         if (err == noErr) err = giveTimeErr;
  541.         if (err != noErr && returnImmediatelyOnError) return err;
  542.     } while (s->pBlock.ioResult > 0);
  543.     if (err == noErr) err = s->pBlock.ioResult;
  544.     if (err == connectionClosing || err == connectionDoesntExist ||
  545.         err == connectionTerminated) s->otherSideHasClosed = true;
  546.     return err;
  547. }
  548.  
  549.  
  550.  
  551. /*----------------------------------------------------------------------------
  552.     MacTCPDNRResultProc 
  553.     
  554.     MacTCP domain name resolver completion routine.
  555.     
  556.     Entry:     hInfoPtr = pointer to hostInfo struct.
  557.             userDataPtr = pointer to user data.
  558. ----------------------------------------------------------------------------*/
  559.  
  560. static pascal void MacTCPDNRResultProc (struct hostInfo *hInfoPtr, char *userDataPtr)
  561. {
  562.     *(Boolean*)userDataPtr = true;
  563. }
  564.  
  565.  
  566.  
  567. /*----------------------------------------------------------------------------
  568.     MacTCPCloseStreamCompletionRoutine
  569.     
  570.     This is the asynchronous MacTCP completion routine used to close streams.
  571.     
  572.     This completion routine chains to itself to do the following tasks
  573.     involved in gracefully tearing down a stream in the background:
  574.     
  575.     Wait for QUIT command send to complete (the send is initiated by the
  576.         NetClose function below).
  577.     Read incoming data until an error occurs (signalling that the
  578.         server has closed its end of the connection).
  579.     Close our end of the connection.
  580.     
  581.     Entry:    pBlock = pointer to MacTCP parameter block.
  582. ----------------------------------------------------------------------------*/
  583.  
  584. static void MacTCPCloseStreamCompletionRoutine (TCPiopb *pBlock)
  585. {
  586.     TStreamPtr s;
  587.     OSErr err = noErr;
  588.     long savedA5;
  589.  
  590.     s = (TStreamPtr)pBlock;
  591.     
  592.     savedA5 = SetA5(s->myA5);
  593.     
  594.     if (s->pBlock.csCode == TCPSend) {
  595.     
  596.         /* The QUIT command has been sent. Start the first receive. */
  597.         
  598.            err = s->pBlock.ioResult;
  599.         if (err != noErr) {
  600.             s->release = true;
  601.             SetA5(savedA5);
  602.             return;
  603.         }
  604.         InitMacTCPParamBlock(&s->pBlock, TCPRcv);
  605.         s->pBlock.csParam.receive.rcvBuff = s->buf;
  606.         s->pBlock.csParam.receive.rcvBuffLen = kBufSize;
  607.     
  608.     } else if (s->pBlock.csCode == TCPRcv) {
  609.     
  610.         /* A receive operation has finished. If there was no error, start another
  611.            receive. If an error occurred, it was because the server has closed
  612.            its side of the connection. In this case, if we have already closed
  613.            our side of the connection, set the "release" flag to signal that
  614.            we can now release the stream and dispose of the queue element. If
  615.            we have not yet closed our side, we start the TCPClose. */
  616.        
  617.            err = s->pBlock.ioResult;
  618.            if (err == noErr) {
  619.                InitMacTCPParamBlock(&s->pBlock, TCPRcv);
  620.             s->pBlock.csParam.receive.rcvBuff = s->buf;
  621.             s->pBlock.csParam.receive.rcvBuffLen = kBufSize;
  622.         } else {
  623.             s->otherSideHasClosed = true;
  624.             if (s->weHaveClosed) {
  625.                 s->release = true;
  626.                 SetA5(savedA5);
  627.                 return;
  628.             } else {
  629.                 InitMacTCPParamBlock(&s->pBlock, TCPClose);
  630.             }
  631.         }
  632.     
  633.     } else if (s->pBlock.csCode == TCPClose) {
  634.     
  635.         /* Our close has finished. If the other side has also closed,
  636.            set the "release" flag to signal that we can now release
  637.            the stream and dispose of the queue element. If the other
  638.            side has not yet closed, issue a receive operation and wait
  639.            for the other side to close. */
  640.  
  641.         s->weHaveClosed = true;
  642.         if (s->otherSideHasClosed) {
  643.             s->release = true;
  644.             SetA5(savedA5);
  645.             return;
  646.         } else {
  647.             InitMacTCPParamBlock(&s->pBlock, TCPRcv);
  648.             s->pBlock.csParam.receive.rcvBuff = s->buf;
  649.             s->pBlock.csParam.receive.rcvBuffLen = kBufSize;
  650.         }
  651.                     
  652.     }
  653.     
  654.     /* Issue the next PBControl call in the chain. */
  655.  
  656.     s->pBlock.ioCompletion = gMacTCPCloseStreamCompletionRoutineUPP;
  657.     s->pBlock.tcpStream = s->tcpStream;
  658.     s->pBlock.ioResult = 1;
  659.     err = PBControlAsync((ParmBlkPtr)&s->pBlock);
  660.     if (err != noErr) s->release = true;
  661.      SetA5(savedA5);
  662.     
  663. }
  664.  
  665.  
  666.  
  667. /*----------------------------------------------------------------------------
  668.     GetMaxTCPMTU 
  669.     
  670.     Get the max MTU size.
  671.     
  672.     Exit:    function result = max MTU, or 0 if error.
  673. ----------------------------------------------------------------------------*/
  674.  
  675. static long GetMaxTCPMTU (void)
  676. {
  677.     UDPiopb iopb;
  678.     OSErr err = noErr;
  679.     
  680.     memset(&iopb, 0, sizeof(iopb));
  681.     err = NetGetMyAddr(&iopb.csParam.mtu.remoteHost);
  682.     if (err != noErr) return 0;
  683.     iopb.ioCRefNum = gRefNum;
  684.     iopb.csCode = UDPMaxMTUSize;
  685.     err = PBControlSync((ParmBlkPtr)&iopb);
  686.     if (err != noErr) return 0;
  687.     return iopb.csParam.mtu.mtuSize;
  688. }
  689.  
  690.  
  691.  
  692. /*----------------------------------------------------------------------------
  693.     DoTCPCreate 
  694.     
  695.     Create a stream.
  696.     
  697.     Entry:    s = pointer to stream.
  698.     
  699.     Exit:    function result = error code.
  700. ----------------------------------------------------------------------------*/
  701.  
  702. static OSErr DoTCPCreate (TStreamPtr s)
  703. {
  704.     OSErr err = noErr;
  705.     OSStatus oterr;
  706.     
  707.     if (gHaveOT) {
  708.     
  709.         s->complete = false;
  710.         err = OTAsyncOpenEndpoint(OTCreateConfiguration(kTCPName), 0,
  711.             nil, MyOTStreamNotifyProc, s);
  712.         if (err != noErr) return err;
  713.         err = MyOTStreamWait(s, false);
  714.         if (err != noErr) return err;
  715.         if (s->code != T_OPENCOMPLETE) return netOpenStreamErr;
  716.         s->ref = s->cookie;
  717.         s->call = OTAlloc(s->ref, T_CALL, T_ADDR, &oterr);
  718.         err = oterr;
  719.         if (err != noErr) goto exit; 
  720.         s->bindReq = OTAlloc(s->ref, T_BIND, T_ADDR, &oterr);
  721.         err = oterr;
  722.         if (err != noErr) goto exit;
  723.         s->bindRet = OTAlloc(s->ref, T_BIND, T_ADDR, &oterr);
  724.         err = oterr;
  725.         if (err != noErr) goto exit;
  726.     
  727.     } else {
  728.     
  729.         InitMacTCPParamBlock(&s->pBlock, TCPCreate);
  730.         s->pBlock.csParam.create.rcvBuff = s->mactcpBuf;
  731.         s->pBlock.csParam.create.rcvBuffLen = gTCPBufSize;
  732.         err = CallMacTCP(s, false);
  733.         if (err != noErr) return err;
  734.         s->tcpStream = s->pBlock.tcpStream;
  735.     }
  736.     
  737.     s->next = gAll;
  738.     gAll = s;
  739.     return noErr;
  740.     
  741. exit:
  742.  
  743.     OTCloseProvider(s->ref);
  744.     return err;
  745. }
  746.  
  747.  
  748.  
  749. /*----------------------------------------------------------------------------
  750.     DoTCPRelease 
  751.     
  752.     Release a stream.
  753.     
  754.     Entry:    s = pointer to stream.
  755.     
  756.     Exit:    function result = error code.
  757.     
  758.     Any active connection is also aborted, if necessary, before releasing 
  759.     the stream.
  760.     
  761.     With MacTCP, the function uses its own TCPiopb parameter block instead 
  762.     of the one inside the stream block, because the one inside the stream block
  763.     may already be in use by some other asynchronous operation.
  764. ----------------------------------------------------------------------------*/
  765.  
  766. static OSErr DoTCPRelease (TStreamPtr s)
  767. {
  768.     TCPiopb pBlock;
  769.     OSErr err = noErr, giveTimeErr = noErr;
  770.     TStreamPtr xs, next, prev = nil;
  771.     Boolean abort;
  772.  
  773.     if (s == nil) return noErr;
  774.     abort = !s->otherSideHasClosed || !s->weHaveClosed;
  775.     
  776.     if (gHaveOT) {
  777.     
  778.         if (abort) OTSndDisconnect(s->ref, nil);
  779.         err = OTCloseProvider(s->ref);
  780.     
  781.     } else {
  782.     
  783.         InitMacTCPParamBlock(&pBlock, TCPRelease);
  784.         pBlock.tcpStream = s->tcpStream;
  785.         PBControlAsync((ParmBlkPtr)&pBlock);
  786.         while (pBlock.ioResult > 0) {
  787.             giveTimeErr = (*gGiveTime)();
  788.             if (err == noErr) err = giveTimeErr;
  789.         }
  790.         if (err == noErr) err = pBlock.ioResult;
  791.     }
  792.     
  793.     for (xs = gAll; xs != nil; xs = next) {
  794.         next = xs->next;
  795.         if (xs == s) {
  796.             if (prev == nil) {
  797.                 gAll = next;
  798.             } else {
  799.                 prev->next = next;
  800.             }
  801.             break;
  802.         }
  803.         prev = xs;
  804.     }
  805.     
  806.     if (gLog != nil) LogMessage(s, ' ', abort ? "Stream aborted." : "Stream released.");
  807.     return err;
  808. }
  809.  
  810.  
  811.  
  812. /*----------------------------------------------------------------------------
  813.     DoTCPActiveOpen 
  814.     
  815.     Open an active stream.
  816.     
  817.     Entry:    s = pointer to stream.
  818.             addr = IP address of server.
  819.             port = port number of service.
  820.     
  821.     Exit:    function result = error code.
  822. ----------------------------------------------------------------------------*/
  823.  
  824. static OSErr DoTCPActiveOpen (TStreamPtr s, unsigned long addr, 
  825.     unsigned short port)
  826. {
  827.     OSErr err = noErr;
  828.     InetAddress *inetAddr;
  829.     
  830.     if (gHaveOT) {
  831.     
  832.         s->complete = false;
  833.         err = OTBind(s->ref, nil, s->bindRet);
  834.         if (err != noErr) return err;
  835.         err = MyOTStreamWait(s, true);
  836.         if (err != noErr) return err;
  837.         if (s->code != T_BINDCOMPLETE) return netOpenStreamErr;
  838.         inetAddr = (InetAddress*)s->bindRet->addr.buf;
  839.         s->localPort = inetAddr->fPort;
  840.         OTInitInetAddress((InetAddress*)s->call->addr.buf, port, addr);
  841.         s->call->addr.len = sizeof(InetAddress);
  842.         s->complete = false;
  843.         err = OTConnect(s->ref, s->call, nil);
  844.         if (err != noErr && err != kOTNoDataErr) 
  845.             return s->otherSideHasClosed ? netOpenStreamErr : err;
  846.         err = MyOTStreamWait(s, true);
  847.         if (err != noErr)
  848.             return s->otherSideHasClosed ? netOpenStreamErr : err;
  849.         if (s->code != T_CONNECT) return netOpenStreamErr;
  850.         err = OTRcvConnect(s->ref, nil);
  851.         if (err != noErr) return err;
  852.  
  853.     } else {
  854.     
  855.         InitMacTCPParamBlock(&s->pBlock, TCPActiveOpen);
  856.         s->pBlock.tcpStream = s->tcpStream;
  857.         s->pBlock.csParam.open.remoteHost = addr;
  858.         s->pBlock.csParam.open.remotePort = port;
  859.         err = CallMacTCP(s, true);
  860.         if (err == noErr) {
  861.             s->localPort = s->pBlock.csParam.open.localPort;
  862.         } else {
  863.             return s->otherSideHasClosed ? netOpenStreamErr : err;
  864.         }
  865.     }
  866.     
  867.     if (gLog != nil) LogMessage(s, ' ', "Stream opened.");
  868.     return noErr;
  869. }
  870.  
  871.  
  872.  
  873. /*----------------------------------------------------------------------------
  874.     DoTCPPassiveOpen 
  875.     
  876.     Open a passive stream.
  877.     
  878.     Entry:    s = pointer to stream.
  879.     
  880.     Exit:    function result = error code.
  881.             *port = assigned unused local port number.
  882.             
  883.     Note: Unlike the other "DoTCPxxx" functions, DoTCPPassiveOpen is
  884.     asynchronous. The passive stream is opened, but the function does not
  885.     wait for another host to connect. The function is used by 
  886.     NetFTPDataPassiveOpen to open passive FTP data streams. The caller must 
  887.     call NetFTPDataWaitForConnection to wait for the FTP server to connect
  888.     to the stream.
  889. ----------------------------------------------------------------------------*/
  890.  
  891. static OSErr DoTCPPassiveOpen (TStreamPtr s, unsigned short *port)
  892. {
  893.     OSErr err = noErr;
  894.     InetAddress *inetAddr;
  895.     
  896.     if (gHaveOT) {
  897.     
  898.         s->bindReq->addr.len = 0;
  899.         s->bindReq->qlen = 1;
  900.         s->complete = false;
  901.         err = OTBind(s->ref, s->bindReq, s->bindRet);
  902.         if (err != noErr) return err;
  903.         err = MyOTStreamWait(s, true);
  904.         if (err != noErr) return err;
  905.         if (s->code != T_BINDCOMPLETE) return netOpenStreamErr;
  906.         inetAddr = (InetAddress*)s->bindRet->addr.buf;
  907.         *port = s->localPort = inetAddr->fPort;
  908.         return noErr;
  909.     
  910.     } else {
  911.     
  912.         InitMacTCPParamBlock(&s->pBlock, TCPPassiveOpen);
  913.         s->pBlock.tcpStream = s->tcpStream;
  914.         PBControlAsync((ParmBlkPtr)&s->pBlock);
  915.         while (s->pBlock.csParam.open.localPort == 0) {
  916.             err = (*gGiveTime)();
  917.             if (err != noErr) return err;
  918.         }
  919.         *port = s->localPort = s->pBlock.csParam.open.localPort;
  920.         return noErr;
  921.         
  922.     }
  923. }
  924.  
  925.  
  926.  
  927. /*----------------------------------------------------------------------------
  928.     DoTCPSend 
  929.     
  930.     Send data on a stream.
  931.     
  932.     Entry:    s = pointer to stream.
  933.             data = pointer to data to send.
  934.             len = length of data to send.
  935.             push = true to set push data flag. This flag should be set on
  936.                 the last buffer of data being sent on the stream before
  937.                 a response from the server is expected. This flag is need
  938.                 to keep IBM VM/CMS happy. (Note OT handles this internally,
  939.                 so this is needed only with MacTCP.)
  940.     
  941.     Exit:    function result = error code.
  942. ----------------------------------------------------------------------------*/
  943.  
  944. static OSErr DoTCPSend (TStreamPtr s, Ptr data, unsigned short len, Boolean push)
  945. {
  946.     OSErr err = noErr;
  947.     OTResult result;
  948.  
  949.     s->in = s->out = s->buf;
  950.     
  951.     if (gHaveOT) {
  952.     
  953.         while (len > 0) {
  954.             err = (*gGiveTime)();
  955.             if (err != noErr) return err;
  956.             result = OTSnd(s->ref, data, len, 0);
  957.             if (result >= 0) {
  958.                 len -= result;
  959.                 s->bytesOut += result;
  960.             } else if (result != kOTFlowErr) {
  961.                 return s->otherSideHasClosed ? netLostConnectionErr : result;
  962.             }
  963.         }
  964.         return noErr;
  965.     
  966.     } else {
  967.     
  968.         s->wds[0].ptr = data;
  969.         s->wds[0].length = len;
  970.         InitMacTCPParamBlock(&s->pBlock, TCPSend);
  971.         s->pBlock.tcpStream = s->tcpStream;
  972.         s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
  973.         s->pBlock.csParam.send.pushFlag = push;
  974.         err = CallMacTCP(s, true);
  975.         if (err == noErr) s->bytesOut += len;
  976.         return s->otherSideHasClosed ? netLostConnectionErr : err;
  977.     
  978.     }
  979. }
  980.  
  981.  
  982.  
  983. /*----------------------------------------------------------------------------
  984.     DoTCPRcv 
  985.     
  986.     Reveive data on a stream.
  987.     
  988.     Entry:    s = pointer to stream.
  989.             data = pointer to data buffer.
  990.             *len = length of data buffer.
  991.     
  992.     Exit:    function result = error code.
  993.             *len = number of bytes received.
  994. ----------------------------------------------------------------------------*/
  995.  
  996. static OSErr DoTCPRcv (TStreamPtr s, Ptr data, unsigned short *len)
  997. {
  998.     OSErr err = noErr;
  999.     OTResult result;
  1000.     
  1001.     if (gHaveOT) {
  1002.     
  1003.         err = (*gGiveTime)();
  1004.         if (err != noErr) return err;
  1005.         result = OTRcv(s->ref, data, *len, nil);
  1006.         if (result >= 0) {
  1007.             *len = result;
  1008.             s->bytesIn += result;
  1009.             return noErr;
  1010.         } else if (s->otherSideHasClosed) {
  1011.             return netLostConnectionErr;
  1012.         } else if (result == kOTNoDataErr) {
  1013.             *len = 0;
  1014.             return noErr;
  1015.         } else {
  1016.             return result;
  1017.         }
  1018.     
  1019.     } else {
  1020.     
  1021.         InitMacTCPParamBlock(&s->pBlock, TCPRcv);
  1022.         s->pBlock.tcpStream = s->tcpStream;
  1023.         s->pBlock.csParam.receive.rcvBuff = StripAddress(data);
  1024.         s->pBlock.csParam.receive.rcvBuffLen = *len;
  1025.         err = CallMacTCP(s, true);
  1026.         *len = s->pBlock.csParam.receive.rcvBuffLen;
  1027.         if (err == noErr) s->bytesIn += *len;
  1028.         return err != noErr && s->otherSideHasClosed ? netLostConnectionErr : err;
  1029.     
  1030.     }
  1031. }
  1032.  
  1033.  
  1034.  
  1035. /*----------------------------------------------------------------------------
  1036.     MyExitToShell 
  1037.     
  1038.     ExitToShell trap patch (for MacTCP only).
  1039.     
  1040.     This patch makes sure that all open streams are closed when ExitToShell
  1041.     is called (e.g., if the program crashes and you type "es" in MacsBug, or
  1042.     if you use Command-Option-Escape to force quit the program). This keeps 
  1043.     MacTCP happy. It doesn't always work, but it helps sometimes.
  1044.     
  1045.     Open Transport has its own mechanisms for doing this, so this patch
  1046.     is only installed if we are using MacTCP.
  1047. ----------------------------------------------------------------------------*/
  1048.  
  1049. static void MyExitToShell (void)
  1050. {
  1051.     TStreamPtr s;
  1052.  
  1053.     SetCurrentA5();
  1054.     SetToolTrapAddress(gOldExitToShellUPP, _ExitToShell);
  1055.     gLog = nil;
  1056.     for (s = gAll; s != nil; s = s->next) DoTCPRelease(s);
  1057.     ExitToShell();
  1058. }
  1059.  
  1060.  
  1061.  
  1062. /*----------------------------------------------------------------------------
  1063.     PatchExitToShell 
  1064.     
  1065.     Install a patch on the ExitToShell trap (for MacTCP only).
  1066. ----------------------------------------------------------------------------*/
  1067.  
  1068. static void PatchExitToShell (void)
  1069. {
  1070.     gOldExitToShellUPP = GetToolTrapAddress(_ExitToShell);
  1071.     gMyExitToShellUPP = 
  1072.         NewRoutineDescriptor((ProcPtr)MyExitToShell, kPascalStackBased, GetCurrentISA());
  1073.     SetToolTrapAddress(gMyExitToShellUPP, _ExitToShell);
  1074. }
  1075.  
  1076.  
  1077.  
  1078. /*----------------------------------------------------------------------------
  1079.     SendCommand 
  1080.     
  1081.     Send a command on a stream.
  1082.     
  1083.     Entry:    s = pointer to stream.
  1084.             command = C-format command string.
  1085.     
  1086.     Exit:    function result = error code.
  1087. ----------------------------------------------------------------------------*/
  1088.  
  1089. static OSErr SendCommand (TStreamPtr s, char *command)
  1090. {
  1091.     OSErr err = noErr;
  1092.     unsigned short len;
  1093.  
  1094.     strcpy(s->command, command);
  1095.     len = strlen(command);
  1096.     BlockMoveData(command, s->buf, len);
  1097.     *(s->buf + len) = CR;
  1098.     *(s->buf + len + 1) = LF;
  1099.     err = DoTCPSend(s, s->buf, len+2, true);
  1100.     if (err != noErr) return err;
  1101.     if (gLog != nil) LogMessage(s, 'C', command);
  1102.     return noErr;
  1103. }
  1104.  
  1105.  
  1106.  
  1107. /*----------------------------------------------------------------------------
  1108.     ReadResponse 
  1109.     
  1110.     Read a command response from a stream.
  1111.     
  1112.     Entry:    s = pointer to stream.
  1113.     
  1114.     Exit:    function result = error code.
  1115.             *responseCode = server response code.
  1116.             response = C-format server response string, including the 
  1117.                 response code.
  1118.                 
  1119.     Response comment lines are discarded (response lines with a
  1120.     '-' immediately following the response code).
  1121. ----------------------------------------------------------------------------*/
  1122.  
  1123. static OSErr ReadResponse (TStreamPtr s, long *responseCode, CStr255 response)
  1124. {
  1125.     char *in, *out, *p, *q, *r;
  1126.     unsigned short len, numUnread;
  1127.     long num;
  1128.     OSErr err = noErr;
  1129.     
  1130.     in = s->in;
  1131.     out = s->out;
  1132.     
  1133.     while (true) {
  1134.         while (out < in) {
  1135.             for (p = out; p < in; p++) {
  1136.                 if (*p == LF) {
  1137.                     *p = 0;
  1138.                     q = out;
  1139.                     out = p+1;
  1140.                     break;
  1141.                 } else if (*p == CR && p < in-1 && *(p+1) == LF) {
  1142.                     *p = 0;
  1143.                     q = out;
  1144.                     out = p+2;
  1145.                     break;
  1146.                 }
  1147.             }
  1148.             if (p >= in) break;
  1149.             r = q;
  1150.             if (*r == '-') {
  1151.                 r++;
  1152.                 num = -CrackNum(&r);
  1153.             } else {
  1154.                 num = CrackNum(&r);
  1155.             }
  1156.             if (*r != '-' && num != 0) {
  1157.                 if (p - q > 255) *(q+255) = 0;
  1158.                 strcpy(response, q);
  1159.                 strcpy(s->response, q);
  1160.                 *responseCode = s->responseCode = num;
  1161.                 s->in = in;
  1162.                 s->out = out;
  1163.                 if (gLog != nil) LogMessage(s, 'R', response);
  1164.                 return noErr;
  1165.             }
  1166.         }
  1167.         if (out == in) {
  1168.             out = in = s->buf;
  1169.         } else if (in > s->buf + kBufSizeDiv2) {
  1170.             numUnread = in - out;
  1171.             if (numUnread > kBufSizeDiv2) {
  1172.                 numUnread = kBufSizeDiv2;
  1173.                 out = in - numUnread;
  1174.             }
  1175.             BlockMoveData(out, s->buf, numUnread);
  1176.             out = s->buf;
  1177.             in = out + numUnread;
  1178.         }
  1179.         len = s->buf + kBufSize - in;
  1180.         err = DoTCPRcv(s, in, &len);
  1181.         if (err != noErr) return err;
  1182.         in += len;
  1183.     }
  1184. }
  1185.  
  1186.  
  1187.  
  1188. /*----------------------------------------------------------------------------
  1189.     MungeOut 
  1190.     
  1191.     Munge a block of text before sending it out to a server.
  1192.     
  1193.     Entry:    text = handle to CR-terminated ASCII text lines.
  1194.                 Warning: the memory block is modified by the function.
  1195.                 The memory block must be unlocked and nonpurgeable.
  1196.             mungePeriods = true to munge periods.
  1197.     
  1198.     Exit:    function result = error code.
  1199.     
  1200.     CR line terminators are translated to CRLF. If the text block does
  1201.     not have a terminating CR at the end of the last line, one is added.
  1202.     
  1203.     If mungePeriods=true, leading "." characters on lines are mapped to 
  1204.     "..", and a terminating "." on a line by itself is added to the end
  1205.     of the text block.
  1206. ----------------------------------------------------------------------------*/
  1207.  
  1208. static OSErr MungeOut (Handle text, Boolean mungePeriods)
  1209. {
  1210.     OSErr err = noErr;
  1211.     char *p, *pEnd, *pStart, *q;
  1212.     long textLen, oldTextLen;
  1213.  
  1214.     textLen = MyGetHandleSize(text);
  1215.     p = *text;
  1216.     pEnd = p + textLen;
  1217.     if (textLen == 0 || *(pEnd-1) != CR) {
  1218.         textLen++;
  1219.         err = MySetHandleSize(text, textLen);
  1220.         if (err != noErr) return err;
  1221.         p = *text;
  1222.         pEnd = p + textLen;
  1223.         *(pEnd-1) = CR;
  1224.     }
  1225.     oldTextLen = textLen;
  1226.     if (mungePeriods) {
  1227.         while (p < pEnd) {
  1228.             if (*p == '.') textLen++;
  1229.             while (*p != CR) p++;
  1230.             p++;
  1231.             textLen++;
  1232.         }
  1233.         textLen += 3;
  1234.     } else {
  1235.         for (; p < pEnd; p++) if (*p == CR) textLen++;
  1236.     }
  1237.     err = MySetHandleSize(text, textLen);
  1238.     if (err != noErr) return err;
  1239.     pStart = *text;
  1240.     p = pStart + oldTextLen - 1;
  1241.     q = pStart + textLen - 1;
  1242.     if (mungePeriods) {
  1243.         *q-- = LF;
  1244.         *q-- = CR;
  1245.         *q-- = '.';
  1246.         while (p >= pStart) {
  1247.             *q-- = LF;
  1248.             *q-- = CR;
  1249.             p--;
  1250.             while (*p != CR && p >= pStart) *q-- = *p--;
  1251.             if (*(p+1) == '.') *q-- = '.';
  1252.         }
  1253.     } else {
  1254.         while (p >= pStart) {
  1255.             if (*p == CR) *q-- = LF;
  1256.             *q-- = *p--;
  1257.         }
  1258.     }
  1259.     return noErr;
  1260. }
  1261.  
  1262.  
  1263.  
  1264. /*----------------------------------------------------------------------------
  1265.     MungeIn 
  1266.     
  1267.     Munge a block of text after receiving it from a server.
  1268.     
  1269.     Entry:    text = handle to ASCII text lines as received from server.
  1270.                 Warning: the memory block is modified by the function.
  1271.                 The memory block must be unlocked and nonpurgeable.
  1272.             textLen = number of characters in text block.
  1273.             mungePeriods = true to munge periods.
  1274.     
  1275.     Exit:    function result = error code.
  1276.     
  1277.     CRLF line terminators are translated to CR. A final CR is added at the
  1278.     end of the last line if necessary.
  1279.     
  1280.     If mungePeriods=true, leading ".." characters on lines are mapped to 
  1281.     ".", and the terminating "." on a line by itself is removed from the end
  1282.     of the text block.
  1283. ----------------------------------------------------------------------------*/
  1284.  
  1285. static OSErr MungeIn (Handle text, long textLen, Boolean mungePeriods)
  1286. {
  1287.     OSErr err = noErr;
  1288.     char *pEnd, *p, *q;
  1289.     Boolean needTerminatingCR = false;
  1290.  
  1291.     if (mungePeriods && textLen >= 3) {
  1292.         p = *text + textLen - 3;
  1293.         if (*p == '.' && *(p+1) == CR && *(p+2) == LF) textLen -= 3;
  1294.     }
  1295.     p = q = *text;
  1296.     pEnd = p + textLen;
  1297.     if (mungePeriods) {
  1298.         while (p < pEnd) {
  1299.             if (*p == '.' && *(p+1) == '.') {
  1300.                 *q++ = '.';
  1301.                 p += 2;
  1302.             }
  1303.             while (p < pEnd && (*p != CR || *(p+1) != LF)) *q++ = *p++;
  1304.             *q++ = CR;
  1305.             p += 2;
  1306.         }
  1307.     } else {
  1308.         while (p < pEnd) {
  1309.             if (*p == CR && *(p+1) == LF) {
  1310.                 *q++ = CR;
  1311.                 p += 2;
  1312.             } else {
  1313.                 *q++ = *p++;
  1314.             }
  1315.         }
  1316.     }
  1317.     textLen = q - *text;
  1318.     if (textLen > 0 && *(q-1) != CR) {
  1319.         textLen++;
  1320.         needTerminatingCR = true;
  1321.     }
  1322.     err = MySetHandleSize(text, textLen);
  1323.     if (err != noErr) return err;
  1324.     if (needTerminatingCR) *(*text + textLen - 1) = CR;
  1325.     return noErr;
  1326. }
  1327.  
  1328.  
  1329.  
  1330. /*----------------------------------------------------------------------------
  1331.     NetInit 
  1332.     
  1333.     Initialize the net.c module. This function must be called before calling
  1334.     any other functions in the module.
  1335.     
  1336.     Entry:    giveTime = pointer to give time function.
  1337.             log = pointer to logging function, or nil if none.
  1338.             numBuffs = number of stream buffers to preallocate.
  1339.     
  1340.     Exit:    function result = error code.
  1341.     
  1342.     The give time function is called repeatedly during all network
  1343.     operations. It must be declared as follows:
  1344.     
  1345.         OSErr GiveTime (void)
  1346.     
  1347.     This function gives time to other processes/threads by calling WaitNextEvent/
  1348.     YieldTo[Any]Thread, and may check for user cancel operations (e.g., clicking
  1349.     a Cancel button or pressing Command-period or Escape). The function returns
  1350.     noErr to continue the operation, or any other error code to cancel (abort)
  1351.     the operation. Any error code returned by GiveTime is in turn returned as the 
  1352.     function result of the functions exported by net.c.
  1353.     
  1354.     The net.c module is reentrant with respect to the GiveTime function. That is,
  1355.     multiple threads of execution may make calls to the net.c module and use
  1356.     the YieldTo[Any]Thread call in the GiveTime function.
  1357.     
  1358.     The logging function is called once for each server command and response
  1359.     and stream open/close operation. It must be declared as follows:
  1360.     
  1361.         void Log (char logEntryType, unsigned long serverAddr, 
  1362.             unsigned short serverPort, unsigned short localPort, char *str)
  1363.         
  1364.     You can use this function to log all sever commands and responses. On
  1365.     entry to the Log function, the parameters are:
  1366.     
  1367.         logEntryType = 
  1368.             'C' if command.
  1369.             'R' if response.
  1370.             ' ' if open/close operation.
  1371.         serverAddr = IP address of server.
  1372.         serverPort = port number on server.
  1373.         localPort = local port number.
  1374.         str = command or response string or open/close message string.
  1375.         
  1376.     The logging function is responsible for filtering out any passwords
  1377.     in the strings.
  1378.     
  1379.     You must call the InitMemUtil function in memutil.c to initialize the
  1380.     memory management utilities *before* calling NetInit.
  1381. ----------------------------------------------------------------------------*/
  1382.  
  1383. OSErr NetInit (NetGiveTimeFunction giveTime, NetLogFunction log, short numBuffs)
  1384. {
  1385.     short i;
  1386.     TStreamPtr s;
  1387.     OSErr err = noErr;
  1388.     long mtu;
  1389.  
  1390.     gHaveOT = NetHaveOT();
  1391.     gGiveTime = giveTime;
  1392.     gLog = log;
  1393.     
  1394.     if (gHaveOT) {
  1395.     
  1396.         err = InitOpenTransport();
  1397.         if (err != noErr) return netOpenDriverErr;
  1398.         gStreamBufSize = sizeof(TStream);
  1399.         
  1400.     } else {
  1401.     
  1402.         gMacTCPDNRResultProcUPP = NewResultProc(MacTCPDNRResultProc);
  1403.         gMacTCPCloseStreamCompletionRoutineUPP = 
  1404.             NewTCPIOCompletionProc(MacTCPCloseStreamCompletionRoutine);
  1405.         err = OpenDriver("\p.IPP", &gRefNum);
  1406.         if (err != noErr) return netOpenDriverErr;
  1407.         mtu = GetMaxTCPMTU();
  1408.         gTCPBufSize = 8 * mtu;
  1409.         if (gTCPBufSize < kMinTCPBufSize) gTCPBufSize = kMinTCPBufSize;
  1410.         gStreamBufSize = sizeof(TStream) - 1 + gTCPBufSize;
  1411.         PatchExitToShell();
  1412.         
  1413.     }
  1414.     
  1415.     for (i = 0; i < numBuffs; i++) {
  1416.         err = MyNewPtr(gStreamBufSize, &s);
  1417.         if (err != noErr) return err;
  1418.         s->nextFree = gFree;
  1419.         gFree = s;
  1420.     }
  1421.     
  1422.     return noErr;
  1423. }
  1424.  
  1425.  
  1426.  
  1427. /*----------------------------------------------------------------------------
  1428.     NetIdle 
  1429.     
  1430.     Handle idle time tasks. You must call this function in your main 
  1431.     event loop.
  1432.     
  1433.     Exit:    function result = error code.
  1434.     
  1435.     This function takes care of releasing and deallocating streams which
  1436.     have finished closing asynchronously in the background.
  1437. ----------------------------------------------------------------------------*/
  1438.  
  1439. OSErr NetIdle (void)
  1440. {
  1441.     TStreamPtr s, nextClose, prev = nil;
  1442.     OSErr err = noErr;
  1443.  
  1444.     for (s = gClose; s != nil; s = nextClose) {
  1445.         nextClose = s->nextClose;
  1446.         if (s->release) {
  1447.             err = DoTCPRelease(s);
  1448.             if (prev == nil) {
  1449.                 gClose = nextClose;
  1450.             } else {
  1451.                 prev->nextClose = nextClose;
  1452.             }
  1453.             DisposeStreamBuffer(s);
  1454.         } else {
  1455.             prev = s;
  1456.         }
  1457.     }
  1458.     return noErr;
  1459. }
  1460.  
  1461.  
  1462.  
  1463. /*----------------------------------------------------------------------------
  1464.     NetTerm 
  1465.     
  1466.     Terminate the module. You must call this function when you quit.
  1467.     
  1468.     Exit:    function result = error code.
  1469.     
  1470.     This functions waits until all asynchronous stream close operations have
  1471.     completed, or for 5 seconds, whichever happens first. If any close
  1472.     operations are still pending after 5 seconds, they are aborted.
  1473.     
  1474.     In addition, any other open streams are aborted.
  1475. ----------------------------------------------------------------------------*/
  1476.  
  1477. OSErr NetTerm (void)
  1478. {
  1479.     TStreamPtr s;
  1480.     long waitTil;
  1481.  
  1482.     waitTil = TickCount() + 300;
  1483.     while (gClose != nil && TickCount() < waitTil) {
  1484.         (*gGiveTime)();
  1485.         NetIdle();
  1486.     }
  1487.     for (s = gAll; s != nil; s = s->next) DoTCPRelease(s);
  1488.     while (gFree != nil) {
  1489.         s = gFree->next;
  1490.         MyDisposePtr(gFree);
  1491.         gFree = s;
  1492.     }
  1493.     gFree = gClose = gAll = nil;
  1494.     if (gHaveOT) CloseOpenTransport();
  1495.      return noErr;
  1496. }
  1497.  
  1498.  
  1499.  
  1500. /*----------------------------------------------------------------------------
  1501.     NetOpen 
  1502.     
  1503.     Open a stream.
  1504.     
  1505.     Entry:    addr = IP address of server.
  1506.             port = port number of service.
  1507.             getHello = true to wait for, receive, and return an initial
  1508.                 "hello" message.
  1509.     
  1510.     Exit:    function result = error code.
  1511.             *stream = opened stream reference.
  1512.             *responseCode = server hello message response code.
  1513.             response = C-format hello message string, including the
  1514.                 response code.
  1515.                 
  1516.     Hello message comment lines are discarded (response lines with a
  1517.     '-' immediately following the response code).
  1518. ----------------------------------------------------------------------------*/
  1519.  
  1520. OSErr NetOpen (unsigned long addr, unsigned short port, Boolean getHello, 
  1521.     NetStreamRef *stream, long *responseCode, CStr255 response)
  1522. {
  1523.     TStreamPtr s;
  1524.     OSErr err = noErr;
  1525.     
  1526.     err = NewStreamBuffer(&s);
  1527.     if (err != noErr) return err;
  1528.     s->in = s->out = s->buf;
  1529.     s->serverAddr = addr;
  1530.     s->serverPort = port;
  1531.     *s->command = *s->response = s->responseCode = 0;
  1532.     err = DoTCPCreate(s);
  1533.     if (err != noErr) goto exit2;
  1534.     err = DoTCPActiveOpen(s, addr, port);
  1535.     if (err != noErr) goto exit1;
  1536.     if (getHello) {
  1537.         err = ReadResponse(s, responseCode, response);
  1538.         if (err != noErr) goto exit1;
  1539.     }
  1540.     *stream = (NetStreamRef)s;
  1541.     return noErr;
  1542.     
  1543. exit1:
  1544.  
  1545.     if (gHaveOT) {
  1546.         if (s->otherSideHasClosed) err = netOpenStreamErr;
  1547.     } else {
  1548.         if (err == connectionClosing || err == connectionDoesntExist ||
  1549.             err == connectionTerminated) err = netOpenStreamErr;
  1550.     }
  1551.     DoTCPRelease(s);
  1552.  
  1553. exit2:
  1554.     
  1555.     DisposeStreamBuffer(s);
  1556.     return TranslateErrorCode(err);
  1557. }
  1558.  
  1559.  
  1560.  
  1561. /*----------------------------------------------------------------------------
  1562.     NetClose 
  1563.     
  1564.     Close a stream.
  1565.     
  1566.     Entry:    stream = stream reference.
  1567.     
  1568.     Exit:    function result = error code.
  1569.     
  1570.     A QUIT command is sent to the server before closing the stream. 
  1571.     Thus you do not need to and should not send this command yourself.
  1572.     
  1573.     To improve performance, all streams are closed asynchronously. 
  1574.     This function returns immediately without any delay.
  1575.     
  1576.     This asynchronous stream closing feature also permits you to close 
  1577.     connections in the background without interfering with or delaying user 
  1578.     actions in the foreground.
  1579. ----------------------------------------------------------------------------*/
  1580.  
  1581. OSErr NetClose (NetStreamRef stream)
  1582. {
  1583.     TStreamPtr s;
  1584.     OSErr err = noErr;
  1585.  
  1586.     s = (TStreamPtr)stream;
  1587.     s->closing = true;
  1588.     
  1589.     /* Link the stream into the queue of closing streams. */
  1590.  
  1591.     s->nextClose = gClose;
  1592.     gClose = s;
  1593.     
  1594.     /* Send the QUIT command. */
  1595.     
  1596.     if (gHaveOT) {
  1597.     
  1598.         if (s->weHaveClosed && s->otherSideHasClosed) {
  1599.             s->release = true;
  1600.             return noErr;
  1601.         } else {
  1602.             err = OTSetBlocking(s->ref);
  1603.             if (err != noErr) goto exit;
  1604.             err = DoTCPSend(s, "QUIT\r\n", 6, true);
  1605.             if (err != noErr) goto exit;
  1606.             err = OTSetNonBlocking(s->ref);
  1607.             if (err != noErr) goto exit;
  1608.         }
  1609.     
  1610.     } else {
  1611.         
  1612.         s->wds[0].length = 6;
  1613.         s->wds[0].ptr = "quit\r\n";
  1614.         InitMacTCPParamBlock(&s->pBlock, TCPSend);
  1615.         s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
  1616.         s->pBlock.ioCompletion = gMacTCPCloseStreamCompletionRoutineUPP;
  1617.         s->pBlock.tcpStream = s->tcpStream;
  1618.         err = PBControlAsync((ParmBlkPtr)&s->pBlock);
  1619.         if (err != noErr) goto exit;
  1620.     
  1621.     }
  1622.     
  1623.     if (gLog != nil) {
  1624.         LogMessage(s, 'C', "QUIT");
  1625.         LogMessage(s, ' ', "Asynch stream close initiated.");
  1626.     }
  1627.     return noErr;
  1628.  
  1629. exit:
  1630.  
  1631.     s->release = true;
  1632.     return TranslateErrorCode(err);
  1633. }
  1634.  
  1635.  
  1636.  
  1637. /*----------------------------------------------------------------------------
  1638.     NetCommand 
  1639.     
  1640.     Send a command to a server and get the response.
  1641.     
  1642.     Entry:    stream = stream reference.
  1643.             command = C-format command string.
  1644.     
  1645.     Exit:    function result = error code.
  1646.             *responseCode = server response code.
  1647.             response = C-format server response string, including the 
  1648.                 response code.
  1649.                 
  1650.     Response comment lines are discarded (response lines with a
  1651.     '-' immediately following the response code).
  1652. ----------------------------------------------------------------------------*/
  1653.  
  1654. OSErr NetCommand (NetStreamRef stream, char *command, 
  1655.     long *responseCode, CStr255 response)
  1656. {
  1657.     TStreamPtr s;
  1658.     OSErr err = noErr;
  1659.  
  1660.     s = (TStreamPtr)stream;
  1661.     
  1662.     err = SendCommand(s, command);
  1663.     if (err != noErr) goto exit;
  1664.     err = ReadResponse(s, responseCode, response);
  1665.     if (err != noErr) goto exit;
  1666.     return noErr;
  1667.     
  1668. exit:
  1669.  
  1670.     DoTCPRelease(s);
  1671.     DisposeStreamBuffer(s);
  1672.     return TranslateErrorCode(err);
  1673. }
  1674.  
  1675.  
  1676.  
  1677. /*----------------------------------------------------------------------------
  1678.     NetGetExtraResponse 
  1679.     
  1680.     Get an extra command response from a server.
  1681.     
  1682.     Entry:    stream = stream reference.
  1683.     
  1684.     Exit:    function result = error code.
  1685.             *responseCode = server response code.
  1686.             response = C-format server response string, including the 
  1687.                 response code.
  1688.                 
  1689.     Response comment lines are discarded (response lines with a
  1690.     '-' immediately following the response code).
  1691.     
  1692.     Some servers have commands which return more than one response. The
  1693.     NetCommand function above reads the first response. This function is
  1694.     used to read the additional response(s). The FTP RETR and STOR commands
  1695.     are examples.
  1696. ----------------------------------------------------------------------------*/
  1697.  
  1698. OSErr NetGetExtraResponse (NetStreamRef stream, long *responseCode, 
  1699.     CStr255 response)
  1700. {
  1701.     TStreamPtr s;
  1702.     OSErr err = noErr;
  1703.  
  1704.     s = (TStreamPtr)stream;
  1705.     err = ReadResponse(s, responseCode, response);
  1706.     if (err != noErr) goto exit;
  1707.     return noErr;
  1708.     
  1709. exit:
  1710.  
  1711.     DoTCPRelease(s);
  1712.     DisposeStreamBuffer(s);
  1713.     return TranslateErrorCode(err);
  1714. }
  1715.  
  1716.  
  1717.  
  1718. /*----------------------------------------------------------------------------
  1719.     NetBatchedCommands 
  1720.     
  1721.     Send multiple batched commands to a server and get and process
  1722.     the responses.
  1723.     
  1724.     Entry:    stream = stream reference.
  1725.             commands = handle to CR-terminated commands.
  1726.                 Warning: the memory block is modified by the function.
  1727.                 The memory block must be unlocked and nonpurgeable.
  1728.             doOneResponse = pointer to response processing function.
  1729.             userDataPtr = pointer to user data to be passed through
  1730.                 to the doOneResponse function.
  1731.     
  1732.     Exit:    function result = error code.
  1733.     
  1734.     The response processing function must be declared as follows:
  1735.  
  1736.         void DoOneResponse (long responseCode, CStr255 response, Ptr userDataPtr)
  1737.     
  1738.     The function is called once for each command response. It is passed the 
  1739.     following parameters:
  1740.     
  1741.         responseCode = server response code.
  1742.         response = C-format server response string, including the
  1743.             response code.
  1744.         userDataPtr = user data pointer.
  1745.                 
  1746.     Response comment lines are discarded (leading response lines with a
  1747.     '-' immediately following the response code).
  1748.     
  1749.     CR line terminators are mapped to CRLF line terminators before sending
  1750.     the batched commands.
  1751.     
  1752.     When you know in advance that you need to send a number of commands to a
  1753.     server all in a row, it is usually much more efficient to send them in
  1754.     a batch rather than sending them one at a time using the NetCommand function
  1755.     above. You should take care, however, since not all servers support batched 
  1756.     commands. 
  1757.     
  1758.     All of the batched commands must return only a single response (after
  1759.     skipping any possible comment lines).
  1760. ----------------------------------------------------------------------------*/
  1761.  
  1762. OSErr NetBatchedCommands (NetStreamRef stream, Handle commands, 
  1763.     NetDoOneResponse doOneResponse, Ptr userDataPtr)
  1764. {
  1765.     TStreamPtr s;
  1766.     OSErr err = noErr;
  1767.     char *p, *q, *r;
  1768.     long cmdLen;
  1769.     unsigned short len;
  1770.     long responseCode;
  1771.     CStr255 response;
  1772.     short numCmds, i;
  1773.     char state;
  1774.     
  1775.     state = MyHGetState(commands);
  1776.  
  1777.     err = MungeOut(commands, false);
  1778.     if (err != noErr) goto exit;
  1779.     cmdLen = MyGetHandleSize(commands);
  1780.     
  1781.     s = (TStreamPtr)stream;
  1782.     MyHLock(commands);
  1783.     p = *commands;
  1784.     while (cmdLen > 0) {
  1785.         len = cmdLen > 4000 ? 4000 : cmdLen;
  1786.         q = p + len - 1;
  1787.         while (q > p && (*q != LF || *(q-1) != CR)) q--;
  1788.         if (q == p) {
  1789.             p += len;
  1790.             cmdLen -= len;
  1791.             continue;
  1792.         } else {
  1793.             numCmds = 0;
  1794.             len = q - p + 1;
  1795.             while (q > p) {
  1796.                 if (*q == LF && *(q-1) == CR) {
  1797.                     numCmds++;
  1798.                     q -= 2;
  1799.                 } else {
  1800.                     q--;
  1801.                 }
  1802.             }
  1803.         }
  1804.         err = DoTCPSend(s, StripAddress(p), len, true);
  1805.         if (err != noErr) goto exit;
  1806.         if (gLog != nil) {
  1807.             q = p;
  1808.             for (i = 0; i < numCmds; i++) {
  1809.                 r = q;
  1810.                 while (*r != CR || *(r+1) != LF) r++;
  1811.                 *r = 0;
  1812.                 LogMessage(s, 'C', q);
  1813.                 q = r + 2;
  1814.             }
  1815.         }
  1816.         p += len;
  1817.         cmdLen -= len;
  1818.         while (numCmds--) {
  1819.             err = ReadResponse(s, &responseCode, response);
  1820.             if (err != noErr) goto exit;
  1821.             (*doOneResponse)(responseCode, response, userDataPtr);
  1822.         }
  1823.     }
  1824.     MyHSetState(commands, state);
  1825.     return noErr;
  1826.     
  1827. exit:
  1828.  
  1829.     DoTCPRelease(s);
  1830.     DisposeStreamBuffer(s);
  1831.     MyHSetState(commands, state);
  1832.     return TranslateErrorCode(err);
  1833. }
  1834.  
  1835.  
  1836.  
  1837. /*----------------------------------------------------------------------------
  1838.     NetPutText 
  1839.     
  1840.     Send command text to a server.
  1841.     
  1842.     Entry:    stream = stream reference.
  1843.             text = handle to CR-terminated ASCII text lines.
  1844.                 Warning: the memory block is modified by the function.
  1845.                 The memory block must be unlocked and nonpurgeable.
  1846.     
  1847.     Exit:    function result = error code.
  1848.     
  1849.     CR line terminators are translated to CRLF. A terminating "." on a line
  1850.     by itself is sent. The caller should not include this terminating line
  1851.     in the "text" block. Leading "." characters on lines are mapped to "..".
  1852.     
  1853.     If the text block does not have a terminating CR at the end of the last
  1854.     line, one is added.
  1855. ----------------------------------------------------------------------------*/
  1856.  
  1857. OSErr NetPutText (NetStreamRef stream, Handle text)
  1858. {
  1859.     TStreamPtr s;
  1860.     OSErr err = noErr;
  1861.     char *p;
  1862.     long textLen;
  1863.     unsigned short len;
  1864.     char state;
  1865.     
  1866.     state = MyHGetState(text);
  1867.  
  1868.     err = MungeOut(text, true);
  1869.     if (err != noErr) goto exit;
  1870.     textLen = MyGetHandleSize(text);
  1871.     
  1872.     s = (TStreamPtr)stream;
  1873.     MyHLock(text);
  1874.     p = *text;
  1875.     while (textLen > 0) {
  1876.         len = textLen > 4000 ? 4000 : textLen;
  1877.         err = DoTCPSend(s, StripAddress(p), len, textLen == len);
  1878.         if (err != noErr) goto exit;
  1879.         p += len;
  1880.         textLen -= len;
  1881.     }
  1882.     MyHSetState(text, state);
  1883.     return noErr;
  1884.     
  1885. exit:
  1886.  
  1887.     DoTCPRelease(s);
  1888.     DisposeStreamBuffer(s);
  1889.     MyHSetState(text, state);
  1890.     return TranslateErrorCode(err);
  1891. }
  1892.  
  1893.  
  1894.  
  1895. /*----------------------------------------------------------------------------
  1896.     NetGetText
  1897.     
  1898.     Get response text from a server.
  1899.     
  1900.     Entry:    stream = stream reference.
  1901.             text = pointer to handle in which to return text,
  1902.                 or nil if none.
  1903.             chunkFunction = pointer to chunk processing function, 
  1904.                 or nil if none.
  1905.             userDataPtr = pointer to user data to be passed through
  1906.                 to the chunk processing function, or nil if none.
  1907.     
  1908.     Exit:    function result = error code.
  1909.             *text = handle to received ASCII text lines, if text != nil.
  1910.             
  1911.     In the text returned via the "text" handle (if requested),
  1912.     CRLF line terminators are translated to CR. The server must send a 
  1913.     terminating "." on a line by itself. This terminating line is not 
  1914.     included in the "text" handle returned to the caller. Leading
  1915.     ".." characters on lines are mapped to ".".
  1916.     
  1917.     The chunk processing function, if any, is called every time a new 
  1918.     block of text is received from the server. This function must be 
  1919.     declared as follows:
  1920.     
  1921.     OSErr ProcessTextChunk (Ptr t, long tLen, Ptr userDataPtr,
  1922.         long *truncPos)
  1923.     
  1924.     Entry:    t = pointer to raw text received from server.
  1925.             tLen = length of text received from server.
  1926.             userDataPtr = pointer to user data.
  1927.             
  1928.     Exit:    function result = error code.
  1929.             *truncPos = position at which to truncate text if
  1930.                 error code is netTruncatedErr.
  1931.     
  1932.     If text == nil, the text is processed by the chunk processing function
  1933.     in pieces (e.g., saved to a file as it comes in over the network). In 
  1934.     this case, the text is not accumulated and returned as a whole to the
  1935.     NetGetText caller, and the "t" and "tLen" parameters passed to the
  1936.     chunk processing function are for just the chunk received.
  1937.     
  1938.     If text != nil, the text is accumulated as it is received and returned
  1939.     as a whole to the NetGetText caller, and the "t" and "tLen" parameters
  1940.     passed to the chunk processing function (if any) are for the entire text
  1941.     as accumulated so far.
  1942.                 
  1943.     If the chunk processing function returns with an error code, NetGetText 
  1944.     aborts the stream and returns to the caller immediately with the text handle
  1945.     set to just the text received so far. The error code returned by the
  1946.     chunk processing function is returned to the NetGetText caller as NetGetText's
  1947.     function result.
  1948.     
  1949.     If the chunk processing function returns netTruncatedErr, and if text != nil,
  1950.     the returned text is truncated to "truncPos" bytes, as specified by the
  1951.     chunk processing function, and NetGetText returns the error code netTruncatedErr
  1952.     to its caller.
  1953.     
  1954.     The text passed to the chunk processing function is raw. It contains CRLF 
  1955.     line terminators and doubled leading ".." characters. The terminating "."
  1956.     on a line by itself, however, is not passed to the chunk processing function.
  1957. ----------------------------------------------------------------------------*/
  1958.  
  1959. OSErr NetGetText (NetStreamRef stream, Handle *text,
  1960.     NetChunkFunction chunkFunction, Ptr userDataPtr)
  1961. {
  1962.     TStreamPtr s;
  1963.     OSErr err = noErr;
  1964.     OSErr chunkErr = noErr;
  1965.     Handle t = nil;
  1966.     long tLen = 0;
  1967.     long tAllocated = 0;
  1968.     unsigned short len;
  1969.     char *tEnd;
  1970.     long truncPos;
  1971.     
  1972.     s = (TStreamPtr)stream;
  1973.     tLen = s->in - s->out;
  1974.     tAllocated = tLen + 4005;
  1975.     err = MyNewHandle(tAllocated, &t);
  1976.     if (err != noErr) goto exit;
  1977.     if (tLen > 0) BlockMoveData(s->out, *t, tLen);
  1978.     s->in = s->out = s->buf;
  1979.     
  1980.     while (true) {
  1981.         if (tLen >= 3) {
  1982.             tEnd = *t + tLen;
  1983.             if (*(tEnd-3) == '.' && *(tEnd-2) == CR && *(tEnd-1) == LF) {
  1984.                 if (tLen == 3) break;
  1985.                 if (tLen >= 5 && *(tEnd-5) == CR && *(tEnd-4) == LF) break;
  1986.             }
  1987.         }
  1988.         len = 4000;
  1989.         MyHLock(t);
  1990.         err = DoTCPRcv(s, *t + tLen, &len);
  1991.         if (err != noErr) goto exit;
  1992.         MyHUnlock(t);
  1993.         tLen += len;
  1994.         if (chunkFunction != nil && tLen >= 5) {
  1995.             MyHLock(t);
  1996.             chunkErr = (*chunkFunction)(*t, tLen - 5, userDataPtr, &truncPos);
  1997.             MyHUnlock(t);
  1998.             if (chunkErr != noErr) goto exit2;
  1999.         }
  2000.         if (text == nil) {
  2001.             BlockMoveData(*t + tLen - 5, *t, 5);
  2002.             tLen = 5;
  2003.         } else {
  2004.             if (tAllocated - tLen <= 4000) {
  2005.                 tAllocated += 4000;
  2006.                 err = MySetHandleSize(t, tAllocated);
  2007.                 if (err != noErr) goto exit;
  2008.             }
  2009.         }
  2010.     }
  2011.     
  2012.     if (chunkFunction != nil && tLen > 3) {
  2013.         MyHLock(t);
  2014.         chunkErr = (*chunkFunction)(*t, tLen-3, userDataPtr, &truncPos);
  2015.         MyHUnlock(t);
  2016.         if (chunkErr != noErr) goto exit2;
  2017.     }
  2018.     
  2019.     if (text == nil) {
  2020.         MyDisposeHandle(t);
  2021.     } else {
  2022.         err = MungeIn(t, tLen, true);
  2023.         if (err != noErr) goto exit;
  2024.         *text = t;
  2025.     }
  2026.  
  2027.     return noErr;
  2028.     
  2029. exit:
  2030.  
  2031.     DoTCPRelease(s);
  2032.     DisposeStreamBuffer(s);
  2033.     MyDisposeHandle(t);
  2034.     return TranslateErrorCode(err);
  2035.     
  2036. exit2:
  2037.  
  2038.     if (text == nil) {
  2039.         MyDisposeHandle(t);
  2040.     } else {
  2041.         if (chunkErr == netTruncatedErr) tLen = truncPos;
  2042.         err = MungeIn(t, tLen, true);
  2043.         if (err != noErr) goto exit;
  2044.         *text = t;
  2045.     }
  2046.     DoTCPRelease(s);
  2047.     DisposeStreamBuffer(s);
  2048.     return chunkErr;
  2049. }
  2050.  
  2051.  
  2052.  
  2053. /*----------------------------------------------------------------------------
  2054.     NetFTPDataPassiveOpen 
  2055.     
  2056.     Open an FTP passive data stream.
  2057.     
  2058.     Exit:    function result = error code.
  2059.             *port = assigned unused local port number.
  2060.             *stream = reference to opened FTP data stream.
  2061.     
  2062.     This function returns immediately. It should be followed by a call to
  2063.     NetFTPDataWaitForConnection to wait for the FTP server to open its end of 
  2064.     the data stream connection.
  2065.     
  2066.     It's confusing, but this "passive" stream open function is used for
  2067.     *active* FTP mode data transfers (the client opens a passive stream
  2068.     and waits for the server to connect). For *passive* FTP mode data
  2069.     transfers (the server opens a passive stream and waits for the client
  2070.     to connect), the regular "active" stream open function NetOpen must
  2071.     be used instead.
  2072. ----------------------------------------------------------------------------*/
  2073.  
  2074. OSErr NetFTPDataPassiveOpen (unsigned short *port, NetStreamRef *stream)
  2075. {
  2076.     TStreamPtr s;
  2077.     OSErr err = noErr;
  2078.  
  2079.     err = NewStreamBuffer(&s);
  2080.     if (err != noErr) goto exit2;
  2081.     s->in = s->out = s->buf;
  2082.     err = DoTCPCreate(s);
  2083.     if (err != noErr) goto exit2;
  2084.     err = DoTCPPassiveOpen(s, port);
  2085.     if (err != noErr) goto exit1;
  2086.     *stream = (NetStreamRef)s;
  2087.     return noErr;
  2088.     
  2089. exit1:
  2090.  
  2091.     DoTCPRelease(s);
  2092.     
  2093. exit2:
  2094.     
  2095.     DisposeStreamBuffer(s);
  2096.     return TranslateErrorCode(err);
  2097. }
  2098.  
  2099.  
  2100.  
  2101. /*----------------------------------------------------------------------------
  2102.     NetFTPDataClose
  2103.     
  2104.     Close an FTP data stream.
  2105.     
  2106.     Entry:    stream = FTP data stream reference.
  2107.     
  2108.     Exit:    function result = error code.
  2109.     
  2110.     This function is used to close all FTP data streams, including "passive
  2111.     mode" data streams and "active mode" data streams.
  2112. ----------------------------------------------------------------------------*/
  2113.  
  2114. OSErr NetFTPDataClose (NetStreamRef stream)
  2115. {
  2116.     TStreamPtr s;
  2117.     OSErr err = noErr;
  2118.     
  2119.     s = (TStreamPtr)stream;
  2120.     s->closing = true;
  2121.      
  2122.     /* Link the stream into the queue of closing streams. */
  2123.  
  2124.     s->nextClose = gClose;
  2125.     gClose = s;
  2126.     
  2127.     /* Close our end of the stream. */
  2128.     
  2129.     if (gHaveOT) {
  2130.     
  2131.         if (!s->weHaveClosed) {
  2132.             err = OTSndOrderlyDisconnect(s->ref);
  2133.             if (err != noErr) goto exit;
  2134.             s->weHaveClosed = true;
  2135.         } else if (s->weHaveClosed && s->otherSideHasClosed) {
  2136.             s->release = true;
  2137.         }
  2138.         return noErr;
  2139.     
  2140.     } else {
  2141.         
  2142.         /* Start an asynchronous close operation, chained to our 
  2143.            completion routine. */
  2144.     
  2145.         InitMacTCPParamBlock(&s->pBlock, TCPClose);
  2146.         s->pBlock.ioCompletion = gMacTCPCloseStreamCompletionRoutineUPP;
  2147.         s->pBlock.tcpStream = s->tcpStream;
  2148.         err = PBControlAsync((ParmBlkPtr)&s->pBlock);
  2149.         if (err != noErr) goto exit;
  2150.         if (gLog != nil) LogMessage(s, ' ', "Asynch stream close initiated.");
  2151.         return noErr;
  2152.     
  2153.     }
  2154.     
  2155. exit:
  2156.     
  2157.     s->release = true;
  2158.     return TranslateErrorCode(err);
  2159. }
  2160.  
  2161.  
  2162.  
  2163. /*----------------------------------------------------------------------------
  2164.     NetFTPDataWaitForConnection 
  2165.     
  2166.     Wait for an FTP server to open its end of a passive data stream.
  2167.     
  2168.     Entry:    stream = FTP data stream reference.
  2169.     
  2170.     Exit:    function result = error code.
  2171.     
  2172.     It's confusing, but this "passive" stream wait function is used for
  2173.     *active* FTP mode data transfers (the client opens a passive stream
  2174.     and waits for the server to connect). For *passive* FTP mode data
  2175.     transfers (the server opens a passive stream and waits for the client
  2176.     to connect), the regular "active" stream open function NetOpen must
  2177.     be used instead, and this function is not used.
  2178. ----------------------------------------------------------------------------*/
  2179.  
  2180. OSErr NetFTPDataWaitForConnection (NetStreamRef stream)
  2181. {
  2182.     TStreamPtr s;
  2183.     OSErr err = noErr;
  2184.     InetAddress *inetAddr;
  2185.     
  2186.     s = (TStreamPtr)stream;
  2187.     
  2188.     if (gHaveOT) {
  2189.     
  2190.         while (true) {
  2191.             err = (*gGiveTime)();
  2192.             if (err != noErr) goto exit;
  2193.             err = OTListen(s->ref, s->call);
  2194.             if (err == noErr) break;
  2195.             if (err != kOTNoDataErr) goto exit;
  2196.         }
  2197.         inetAddr = (InetAddress*)s->call->addr.buf;
  2198.         s->serverAddr = inetAddr->fHost;
  2199.         s->serverPort = inetAddr->fPort;
  2200.         s->complete = false;
  2201.         err = OTAccept(s->ref, s->ref, s->call);
  2202.         if (err != noErr) goto exit;
  2203.         err = MyOTStreamWait(s, true);
  2204.         if (err != noErr) goto exit;
  2205.     
  2206.     } else {
  2207.     
  2208.         while (s->pBlock.ioResult > 0) {
  2209.             err = (*gGiveTime)();
  2210.             if (err != noErr) goto exit;
  2211.         }
  2212.         err = s->pBlock.ioResult;
  2213.         if (err != noErr) goto exit;
  2214.         s->serverAddr = s->pBlock.csParam.open.remoteHost;
  2215.         s->serverPort = s->pBlock.csParam.open.remotePort;
  2216.         
  2217.     }
  2218.     
  2219.     if (gLog != nil) LogMessage(s, ' ', "Stream opened.");
  2220.     return noErr;
  2221.         
  2222. exit:
  2223.  
  2224.     if (gHaveOT) {
  2225.         if (s->otherSideHasClosed) err = netOpenStreamErr;
  2226.     } else {
  2227.         if (err == connectionClosing || err == connectionDoesntExist ||
  2228.             err == connectionTerminated) err = netOpenStreamErr;
  2229.     }
  2230.     DoTCPRelease(s);
  2231.     DisposeStreamBuffer(s);
  2232.     return TranslateErrorCode(err);
  2233. }
  2234.  
  2235.  
  2236.  
  2237. /*----------------------------------------------------------------------------
  2238.     NetGetFTPData 
  2239.     
  2240.     Get data from an FTP server.
  2241.     
  2242.     Entry:    stream = FTP data stream reference.
  2243.             mapCR = true to map CRLF line terminators to CR.
  2244.     
  2245.     Exit:    function result = error code.
  2246.             *data = handle to received data.
  2247.             
  2248. ----------------------------------------------------------------------------*/
  2249.  
  2250. OSErr NetGetFTPData (NetStreamRef stream, Boolean mapCR, Handle *data)
  2251. {
  2252.     TStreamPtr s;
  2253.     OSErr err = noErr;
  2254.     Handle d = nil;
  2255.     long dLen = 0;
  2256.     long dAllocated = 4000;
  2257.     long numFree;
  2258.     unsigned short len;
  2259.     
  2260.     s = (TStreamPtr)stream;
  2261.     err = MyNewHandle(dAllocated, &d);
  2262.     if (err != noErr) goto exit;
  2263.     
  2264.     while (true) {
  2265.         numFree = dAllocated - dLen;
  2266.         if (numFree <= 4000) {
  2267.             dAllocated += 4000;
  2268.             err = MySetHandleSize(d, dAllocated);
  2269.             if (err != noErr) goto exit;
  2270.         }
  2271.         len = 4000;
  2272.         MyHLock(d);
  2273.         err = DoTCPRcv(s, *d + dLen, &len);
  2274.         MyHUnlock(d);
  2275.         if (err == netLostConnectionErr) break;
  2276.         if (err != noErr) goto exit;
  2277.         dLen += len; 
  2278.     }
  2279.     
  2280.     if (mapCR) {
  2281.         err = MungeIn(d, dLen, false);
  2282.         if (err != noErr) goto exit;
  2283.     } else {
  2284.         MySetHandleSize(d, dLen);
  2285.     }
  2286.     
  2287.     *data = d;
  2288.     return noErr;
  2289.     
  2290. exit:
  2291.  
  2292.     DoTCPRelease(s);
  2293.     DisposeStreamBuffer(s);
  2294.     MyDisposeHandle(d);
  2295.     return TranslateErrorCode(err);
  2296. }
  2297.  
  2298.  
  2299.  
  2300. /*----------------------------------------------------------------------------
  2301.     NetPutFTPData 
  2302.     
  2303.     Send data to an FTP server.
  2304.     
  2305.     Entry:    stream = FTP data stream reference.
  2306.             mapCR = true to map CR line terminators to CRLF.
  2307.             data = handle to data.
  2308.     
  2309.     Exit:    function result = error code.
  2310.  
  2311.     Warning: If mapCR = true, the data block is modified by the function,
  2312.     and it must be unlocked and nonpurgeable.
  2313. ----------------------------------------------------------------------------*/
  2314.  
  2315. OSErr NetPutFTPData (NetStreamRef stream, Boolean mapCR, Handle data)
  2316. {
  2317.     TStreamPtr s;
  2318.     OSErr err = noErr;
  2319.     char *p;
  2320.     long dataLen;
  2321.     unsigned short len;
  2322.     char state;
  2323.     
  2324.     state = MyHGetState(data);
  2325.  
  2326.     if (mapCR) {
  2327.         err = MungeOut(data, false);
  2328.         if (err != noErr) goto exit;
  2329.     }
  2330.     
  2331.     dataLen = MyGetHandleSize(data);
  2332.     
  2333.     s = (TStreamPtr)stream;
  2334.     MyHLock(data);
  2335.     p = *data;
  2336.     while (dataLen > 0) {
  2337.         len = dataLen > 4000 ? 4000 : dataLen;
  2338.         err = DoTCPSend(s, StripAddress(p), len, dataLen == len);
  2339.         if (err != noErr) goto exit;
  2340.         p += len;
  2341.         dataLen -= len;
  2342.     }
  2343.     MyHSetState(data, state);
  2344.     return noErr;
  2345.     
  2346. exit:
  2347.  
  2348.     DoTCPRelease(s);
  2349.     DisposeStreamBuffer(s);
  2350.     MyHSetState(data, state);
  2351.     return TranslateErrorCode(err);
  2352. }
  2353.  
  2354.  
  2355.  
  2356. /*----------------------------------------------------------------------------
  2357.     NetGetMyAddr 
  2358.     
  2359.     Get this Mac's IP address.
  2360.     
  2361.     Exit:    function result = error code.
  2362.             *addr = the IP address of this Mac.
  2363.             
  2364.     With Open Transport, if the Mac has more than one IP interface, the
  2365.     IP address of the default interface is returned.
  2366. ----------------------------------------------------------------------------*/
  2367.  
  2368. OSErr NetGetMyAddr (unsigned long *addr)
  2369. {
  2370.     struct GetAddrParamBlock pBlock;
  2371.     OSErr err = noErr, giveTimeErr = noErr;
  2372.     static Boolean gotIt = false;
  2373.     static unsigned long myAddr;
  2374.     InetInterfaceInfo ifaceInfo;
  2375.     
  2376.     if (!gotIt) {
  2377.     
  2378.         if (gHaveOT) {
  2379.         
  2380.             err = OTInetGetInterfaceInfo(&ifaceInfo, kDefaultInetInterface);
  2381.             if (err != noErr) return TranslateErrorCode(err);
  2382.             myAddr = ifaceInfo.fAddress;
  2383.         
  2384.         } else {
  2385.         
  2386.             memset(&pBlock, 0, sizeof(pBlock));
  2387.             pBlock.ioResult = 1;
  2388.             pBlock.csCode = ipctlGetAddr;
  2389.             pBlock.ioCRefNum = gRefNum;
  2390.             PBControlAsync((ParmBlkPtr)&pBlock);
  2391.             while (pBlock.ioResult > 0) {
  2392.                 giveTimeErr = (*gGiveTime)();
  2393.                 if (err == noErr) err = giveTimeErr;
  2394.             }
  2395.             if (err != noErr) return TranslateErrorCode(err);
  2396.             err = pBlock.ioResult;
  2397.             if (err != noErr) return TranslateErrorCode(err);
  2398.             myAddr = pBlock.ourAddress;
  2399.             
  2400.         }
  2401.         
  2402.         gotIt = true;
  2403.     }
  2404.     
  2405.     *addr = myAddr;
  2406.     return noErr;
  2407. }
  2408.  
  2409.  
  2410.  
  2411. /*----------------------------------------------------------------------------
  2412.     NetGetMyAddrStr 
  2413.     
  2414.     Get this Mac's IP address as a dotted-decimal string
  2415.  
  2416.     Exit:    function result = error code.
  2417.             name = this Mac's IP address, as a C-format string.
  2418.                 You must allocate at least 16 bytes for this string.
  2419.                 The returned string has max length 15.
  2420. ----------------------------------------------------------------------------*/
  2421.     
  2422. OSErr NetGetMyAddrStr (char *addrStr)
  2423. {
  2424.     unsigned long addr;
  2425.     OSErr err = noErr;
  2426.     static char theAddrStr[16];
  2427.     static Boolean gotIt=false;
  2428.     
  2429.     if (!gotIt) {
  2430.         err = NetGetMyAddr(&addr);
  2431.         if (err != noErr) return TranslateErrorCode(err);
  2432.         sprintf(theAddrStr, "%ld.%ld.%ld.%ld",
  2433.             (addr >> 24) & 0xff,
  2434.             (addr >> 16) & 0xff,
  2435.             (addr >> 8) & 0xff,
  2436.             addr & 0xff);
  2437.         gotIt = true;
  2438.     }
  2439.     strcpy(addrStr, theAddrStr);
  2440.     return noErr;
  2441. }
  2442.  
  2443.  
  2444.  
  2445. /*----------------------------------------------------------------------------
  2446.     NetGetMyName 
  2447.     
  2448.     Get this Mac's domain name, if any.
  2449.     
  2450.     Exit:    function result = error code.
  2451.             name = domain name of this Mac, as a C-format string.
  2452. ----------------------------------------------------------------------------*/
  2453.  
  2454. OSErr NetGetMyName (CStr255 name)
  2455. {
  2456.     unsigned long addr;
  2457.     short len;
  2458.     static OSErr err = noErr;
  2459.     static Boolean gotIt=false;
  2460.     static CStr255 theName;
  2461.  
  2462.     if (!gotIt) {
  2463.         err = NetGetMyAddr(&addr);
  2464.         if (err != noErr) return TranslateErrorCode(err);
  2465.         err = NetAddrToName(addr, theName);
  2466.         if (err != noErr) return TranslateErrorCode(err);
  2467.         gotIt = true;
  2468.         len = strlen(theName);
  2469.         if (theName[len-1] == '.') theName[len-1] = 0;
  2470.     }
  2471.     strcpy(name, theName);
  2472.     return noErr;
  2473. }
  2474.  
  2475.  
  2476.  
  2477. /*----------------------------------------------------------------------------
  2478.     NetNameToAddr 
  2479.     
  2480.     Translate a domain name to an IP address.
  2481.     
  2482.     Entry:     name = C-format domain name string, optionally followed by a
  2483.                 comma, space, or colon and then the port number.
  2484.             defaultPort = default port number.
  2485.     
  2486.     Exit:    function result = error code.
  2487.             *addr = IP address.
  2488.             *port = port number.
  2489. ----------------------------------------------------------------------------*/
  2490.  
  2491. OSErr NetNameToAddr (char *name, unsigned short defaultPort, 
  2492.     unsigned long *addr, unsigned short *port)
  2493. {
  2494.     OSErr err = noErr, giveTimeErr = noErr;
  2495.     short i;
  2496.     static struct {
  2497.         CStr255 domainName;
  2498.         unsigned long addr;
  2499.     } cache[10];
  2500.     static short numCache = 0;
  2501.     struct hostInfo hInfoMacTCP;
  2502.     InetHostInfo hInfoOT;
  2503.     Boolean done = false;
  2504.     CStr255 domainName;
  2505.     char *p, *q;
  2506.     TMyOTInetSvcInfo svcInfo;
  2507.     
  2508.     p = name;
  2509.     q = domainName;
  2510.     while (*p != 0 && *p != ',' && *p != ' ' && *p != ':') *q++ = *p++;
  2511.     *q = 0;
  2512.     q = p;
  2513.     while (*q == ' ') q++;
  2514.     if (*q == 0) {
  2515.         *port = defaultPort;
  2516.     } else {
  2517.         p++;
  2518.         if (!isdigit(*p)) return netDNRErr;
  2519.         q = p+1;
  2520.         while (isdigit(*q)) q++;
  2521.         while (*q == ' ') q++;
  2522.         if (*q != 0) return netDNRErr;
  2523.         *port = atoi(p);
  2524.     }
  2525.     
  2526.     for (i=0; i<numCache; i++) {
  2527.         if (strcmp(domainName, cache[i].domainName) == 0) {
  2528.             *addr = cache[i].addr;
  2529.             return noErr;
  2530.         }
  2531.     }
  2532.     
  2533.     if (gHaveOT) {
  2534.     
  2535.         err = OTInetStringToHost(domainName, addr);
  2536.         if (err != noErr) {
  2537.             err = MyOTOpenInetServices(&svcInfo);
  2538.             if (err == kEINVALErr) return netDNRErr;
  2539.             if (err != noErr) return TranslateErrorCode(err);
  2540.             svcInfo.complete = false;
  2541.             err = OTInetStringToAddress(svcInfo.ref, domainName, &hInfoOT);
  2542.             if (err == noErr) err = MyOTInetSvcWait(&svcInfo);
  2543.             OTCloseProvider(svcInfo.ref);
  2544.             if (err != noErr) {
  2545.                 if (err == kOTNoDataErr || err == kOTBadNameErr) err = netDNRErr;
  2546.                 return TranslateErrorCode(err);
  2547.             }
  2548.             *addr = hInfoOT.addrs[0];
  2549.         }
  2550.     
  2551.     } else {
  2552.     
  2553.         while (gMacTCPDNROperationInProgress) {
  2554.             /* Some other thread is using the DNR. Wait for it to finish. */
  2555.             err = (*gGiveTime)();
  2556.             if (err != noErr) return err;
  2557.         }
  2558.         err = OpenResolver(nil);
  2559.         if (err != noErr) return TranslateErrorCode(err);
  2560.         memset(&hInfoMacTCP, 0, sizeof(hInfoMacTCP));
  2561.         gMacTCPDNROperationInProgress = true;
  2562.         err = StrToAddr(domainName, &hInfoMacTCP, gMacTCPDNRResultProcUPP, (char*)&done);
  2563.         if (err == cacheFault) {
  2564.             err = noErr;
  2565.             while (!done) {
  2566.                 giveTimeErr = (*gGiveTime)();
  2567.                 if (err == noErr) err = giveTimeErr;
  2568.             }
  2569.             if (err == noErr) err = hInfoMacTCP.rtnCode;
  2570.         }
  2571.         gMacTCPDNROperationInProgress = false;
  2572.         (*gGiveTime)();
  2573.         CloseResolver();
  2574.         if (err != noErr) return TranslateErrorCode(err);
  2575.         *addr = hInfoMacTCP.addr[0];
  2576.     
  2577.     }
  2578.     
  2579.     if (numCache < 10) {
  2580.         strcpy(cache[numCache].domainName, domainName);
  2581.         cache[numCache].addr = *addr;
  2582.         numCache++;
  2583.     }
  2584.     return noErr;
  2585. }
  2586.  
  2587.  
  2588.  
  2589. /*----------------------------------------------------------------------------
  2590.     NetAddrToName 
  2591.     
  2592.     Translate an IP address to a domain name.
  2593.     
  2594.     Entry:    addr = IP address.
  2595.     
  2596.     Exit:    function result = error code.
  2597.             name = domain name, as a C-format string.
  2598. ----------------------------------------------------------------------------*/
  2599.  
  2600. OSErr NetAddrToName (unsigned long addr, CStr255 name)
  2601. {
  2602.     struct hostInfo hInfoMacTCP;
  2603.     OSErr err = noErr, giveTimeErr = noErr;
  2604.     Boolean done=false;
  2605.     TMyOTInetSvcInfo svcInfo;
  2606.     
  2607.     if (gHaveOT) {
  2608.     
  2609.         err = MyOTOpenInetServices(&svcInfo);
  2610.         if (err != noErr) return TranslateErrorCode(err);
  2611.         svcInfo.complete = false;
  2612.         err = OTInetAddressToName(svcInfo.ref, addr, name);
  2613.         if (err == noErr) err = MyOTInetSvcWait(&svcInfo);
  2614.         OTCloseProvider(svcInfo.ref);
  2615.         if (err != noErr) {
  2616.             if (err == kOTNoDataErr || err == kOTBadNameErr) err = netDNRErr;
  2617.             return TranslateErrorCode(err);
  2618.         }
  2619.         return noErr;    
  2620.     
  2621.     } else {
  2622.     
  2623.         while (gMacTCPDNROperationInProgress) {
  2624.             /* Some other thread is using the DNR. Wait for it to finish. */
  2625.             err = (*gGiveTime)();
  2626.             if (err != noErr) return err;
  2627.         }
  2628.         
  2629.         err = OpenResolver(nil);
  2630.         if (err != noErr) return TranslateErrorCode(err);
  2631.         memset(&hInfoMacTCP, 0, sizeof(hInfoMacTCP));
  2632.         
  2633.         gMacTCPDNROperationInProgress = true;
  2634.         err = AddrToName(addr, &hInfoMacTCP, gMacTCPDNRResultProcUPP, (char*)&done);
  2635.         if (err == cacheFault) {
  2636.             err = noErr;
  2637.             while (!done) {
  2638.                 giveTimeErr = (*gGiveTime)();
  2639.                 if (err == noErr) err = giveTimeErr;
  2640.             }
  2641.             if (err == noErr) err = hInfoMacTCP.rtnCode;
  2642.         }
  2643.         gMacTCPDNROperationInProgress = false;
  2644.         (*gGiveTime)();
  2645.         CloseResolver();
  2646.         if (err != noErr) return TranslateErrorCode(err);
  2647.         hInfoMacTCP.cname[254] = 0;
  2648.         strcpy(name, hInfoMacTCP.cname);
  2649.         return noErr;
  2650.         
  2651.     }
  2652. }
  2653.  
  2654.  
  2655.  
  2656. /*----------------------------------------------------------------------------
  2657.     NetMacTCPDNROperationInProgress 
  2658.     
  2659.     Find out whether there is a MacTCP DNR operation in progress.
  2660.     
  2661.     Exit:    function result = true if MacTCP DNR operation in progress.
  2662. ----------------------------------------------------------------------------*/
  2663.  
  2664. Boolean NetMacTCPDNROperationInProgress (void)
  2665. {
  2666.     return gMacTCPDNROperationInProgress;
  2667. }
  2668.  
  2669.  
  2670.  
  2671. /*----------------------------------------------------------------------------
  2672.     NetGetServerErrInfo 
  2673.     
  2674.     Get server error information for a stream.
  2675.     
  2676.     Entry:    stream = stream reference.
  2677.     
  2678.     Exit:    *serverErrInfo = server error informaton.
  2679. ----------------------------------------------------------------------------*/
  2680.  
  2681. void NetGetServerErrInfo (NetStreamRef stream, NetServerErrInfo *serverErrInfo)
  2682. {
  2683.     TStreamPtr s;
  2684.     
  2685.     s = (TStreamPtr)stream;
  2686.     strcpy(serverErrInfo->command, s->command);
  2687.     strcpy(serverErrInfo->response, s->response);
  2688.     serverErrInfo->responseCode = s->responseCode;
  2689. }
  2690.  
  2691.  
  2692.  
  2693. /*----------------------------------------------------------------------------
  2694.     NetGetStreamStats 
  2695.     
  2696.     Get stream stats (bytes in/out).
  2697.     
  2698.     Entry:    stream = stream reference.
  2699.     
  2700.     Exit:    *bytesIn = number of bytes received on stream.
  2701.             *bytesOut = number of bytes sent on stream.
  2702. ----------------------------------------------------------------------------*/
  2703.  
  2704. void NetGetStreamStats (NetStreamRef stream, long *bytesIn, long *bytesOut)
  2705. {
  2706.     TStreamPtr s;
  2707.     
  2708.     s = (TStreamPtr)stream;
  2709.     *bytesIn = s->bytesIn;
  2710.     *bytesOut = s->bytesOut;
  2711. }
  2712.  
  2713.  
  2714.  
  2715. /*----------------------------------------------------------------------------
  2716.     NetHaveOT 
  2717.     
  2718.     Determine whether we have Open Transport.
  2719.     
  2720.     Exit:    function result = true if Open Transport and Open Transport/TCP
  2721.                 are both installed.
  2722.                 
  2723.     Note: Apple says we shouldn't ship 68K OT code yet (not until OT 1.1). 
  2724.     So this function always returns false on 68K Macs for now.
  2725. ----------------------------------------------------------------------------*/
  2726.  
  2727. Boolean NetHaveOT (void)
  2728. {
  2729.     static Boolean gotIt = false;
  2730.     static Boolean haveOT;
  2731.     OSErr err = noErr;
  2732.     long result;
  2733.     
  2734.     #ifndef powerc
  2735.         return false;
  2736.     #endif
  2737.     
  2738.     if (!gotIt) {
  2739.         err = Gestalt(gestaltOpenTpt, &result);
  2740.         haveOT = err == noErr && 
  2741.             (result & gestaltOpenTptPresent) != 0 &&
  2742.             (result & gestaltOpenTptTCPPresent) != 0;
  2743.         gotIt = true;
  2744.     }
  2745.     return haveOT;
  2746. }
  2747.